Merge branch 'develop' into main
This commit is contained in:
commit
eb99751ad9
@ -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)
|
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||||
###############################################################################
|
|
||||||
|
# ##############################################################################
|
||||||
#
|
#
|
||||||
###############################################################################
|
# ##############################################################################
|
||||||
project(dbt-rise-tgc VERSION 1.0.0)
|
project(dbt-rise-tgc VERSION 1.0.0)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
@ -19,33 +20,40 @@ set(LIB_SOURCES
|
|||||||
src/iss/arch/tgc5c.cpp
|
src/iss/arch/tgc5c.cpp
|
||||||
src/vm/interp/vm_tgc5c.cpp
|
src/vm/interp/vm_tgc5c.cpp
|
||||||
src/vm/fp_functions.cpp
|
src/vm/fp_functions.cpp
|
||||||
|
src/iss/semihosting/semihosting.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WITH_TCC)
|
if(WITH_TCC)
|
||||||
list(APPEND LIB_SOURCES
|
list(APPEND LIB_SOURCES
|
||||||
src/vm/tcc/vm_tgc5c.cpp
|
src/vm/tcc/vm_tgc5c.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_LLVM)
|
if(WITH_LLVM)
|
||||||
list(APPEND LIB_SOURCES
|
list(APPEND LIB_SOURCES
|
||||||
src/vm/llvm/vm_tgc5c.cpp
|
src/vm/llvm/vm_tgc5c.cpp
|
||||||
src/vm/llvm/fp_impl.cpp
|
src/vm/llvm/fp_impl.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_ASMJIT)
|
if(WITH_ASMJIT)
|
||||||
list(APPEND LIB_SOURCES
|
list(APPEND LIB_SOURCES
|
||||||
src/vm/asmjit/vm_tgc5c.cpp
|
src/vm/asmjit/vm_tgc5c.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# library files
|
# library files
|
||||||
FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp)
|
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_VM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp)
|
||||||
FILE(GLOB GEN_YAML_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/contrib/instr/*.yaml)
|
FILE(GLOB GEN_YAML_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/contrib/instr/*.yaml)
|
||||||
list(APPEND LIB_SOURCES ${GEN_ISS_SOURCES} ${GEN_VM_SOURCES})
|
list(APPEND LIB_SOURCES ${GEN_ISS_SOURCES} ${GEN_VM_SOURCES})
|
||||||
|
|
||||||
foreach(FILEPATH ${GEN_ISS_SOURCES})
|
foreach(FILEPATH ${GEN_ISS_SOURCES})
|
||||||
get_filename_component(CORE ${FILEPATH} NAME_WE)
|
get_filename_component(CORE ${FILEPATH} NAME_WE)
|
||||||
string(TOUPPER ${CORE} CORE)
|
string(TOUPPER ${CORE} CORE)
|
||||||
list(APPEND LIB_DEFINES CORE_${CORE})
|
list(APPEND LIB_DEFINES CORE_${CORE})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
message(STATUS "Core defines are ${LIB_DEFINES}")
|
message(STATUS "Core defines are ${LIB_DEFINES}")
|
||||||
|
|
||||||
if(WITH_LLVM)
|
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)
|
FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/tcc/vm_*.cpp)
|
||||||
list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
|
list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_ASMJIT)
|
if(WITH_ASMJIT)
|
||||||
FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/asmjit/vm_*.cpp)
|
FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/asmjit/vm_*.cpp)
|
||||||
list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
|
list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TARGET yaml-cpp::yaml-cpp)
|
if(TARGET yaml-cpp::yaml-cpp)
|
||||||
list(APPEND LIB_SOURCES
|
list(APPEND LIB_SOURCES
|
||||||
src/iss/plugin/cycle_estimate.cpp
|
src/iss/plugin/cycle_estimate.cpp
|
||||||
src/iss/plugin/instruction_count.cpp
|
src/iss/plugin/instruction_count.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Define the library
|
# Define the library
|
||||||
add_library(${PROJECT_NAME} SHARED ${LIB_SOURCES})
|
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")
|
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE /wd4293)
|
target_compile_options(${PROJECT_NAME} PRIVATE /wd4293)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC src)
|
target_include_directories(${PROJECT_NAME} PUBLIC src)
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC src-gen)
|
target_include_directories(${PROJECT_NAME} PUBLIC src-gen)
|
||||||
|
|
||||||
target_force_link_libraries(${PROJECT_NAME} PRIVATE dbt-rise-core)
|
target_force_link_libraries(${PROJECT_NAME} PRIVATE dbt-rise-core)
|
||||||
|
|
||||||
# only re-export the include paths
|
# only re-export the include paths
|
||||||
get_target_property(DBT_CORE_INCL dbt-rise-core INTERFACE_INCLUDE_DIRECTORIES)
|
get_target_property(DBT_CORE_INCL dbt-rise-core INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
target_include_directories(${PROJECT_NAME} INTERFACE ${DBT_CORE_INCL})
|
target_include_directories(${PROJECT_NAME} INTERFACE ${DBT_CORE_INCL})
|
||||||
get_target_property(DBT_CORE_DEFS dbt-rise-core INTERFACE_COMPILE_DEFINITIONS)
|
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})
|
target_compile_definitions(${PROJECT_NAME} INTERFACE ${DBT_CORE_DEFS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC elfio::elfio softfloat scc-util Boost::coroutine)
|
target_link_libraries(${PROJECT_NAME} PUBLIC elfio::elfio softfloat scc-util Boost::coroutine)
|
||||||
|
|
||||||
if(TARGET yaml-cpp::yaml-cpp)
|
if(TARGET yaml-cpp::yaml-cpp)
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_PLUGINS)
|
target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_PLUGINS)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC yaml-cpp::yaml-cpp)
|
target_link_libraries(${PROJECT_NAME} PUBLIC yaml-cpp::yaml-cpp)
|
||||||
@ -97,8 +112,9 @@ if(WITH_LLVM)
|
|||||||
find_package(LLVM)
|
find_package(LLVM)
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC ${LLVM_DEFINITIONS})
|
target_compile_definitions(${PROJECT_NAME} PUBLIC ${LLVM_DEFINITIONS})
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC ${LLVM_INCLUDE_DIRS})
|
target_include_directories(${PROJECT_NAME} PUBLIC ${LLVM_INCLUDE_DIRS})
|
||||||
|
|
||||||
if(BUILD_SHARED_LIBS)
|
if(BUILD_SHARED_LIBS)
|
||||||
target_link_libraries( ${PROJECT_NAME} PUBLIC ${LLVM_LIBRARIES})
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${LLVM_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -119,16 +135,18 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss COMPONENT ${PROJECT_NAME}
|
|||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # target directory
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # target directory
|
||||||
FILES_MATCHING # install only matched files
|
FILES_MATCHING # install only matched files
|
||||||
PATTERN "*.h" # select header files
|
PATTERN "*.h" # select header files
|
||||||
)
|
)
|
||||||
install(FILES ${GEN_YAML_SOURCES} DESTINATION share/tgc-vp)
|
install(FILES ${GEN_YAML_SOURCES} DESTINATION share/tgc-vp)
|
||||||
###############################################################################
|
|
||||||
|
# ##############################################################################
|
||||||
#
|
#
|
||||||
###############################################################################
|
# ##############################################################################
|
||||||
set(CMAKE_INSTALL_RPATH $ORIGIN/../${CMAKE_INSTALL_LIBDIR})
|
set(CMAKE_INSTALL_RPATH $ORIGIN/../${CMAKE_INSTALL_LIBDIR})
|
||||||
project(tgc-sim)
|
project(tgc-sim)
|
||||||
find_package(Boost COMPONENTS program_options thread REQUIRED)
|
find_package(Boost COMPONENTS program_options thread REQUIRED)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} src/main.cpp)
|
add_executable(${PROJECT_NAME} src/main.cpp)
|
||||||
|
|
||||||
if(TARGET ${CORE_NAME}_cpp)
|
if(TARGET ${CORE_NAME}_cpp)
|
||||||
list(APPEND TGC_SOURCES ${${CORE_NAME}_OUTPUT_FILES})
|
list(APPEND TGC_SOURCES ${${CORE_NAME}_OUTPUT_FILES})
|
||||||
else()
|
else()
|
||||||
@ -140,21 +158,20 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
foreach(F IN LISTS TGC_SOURCES)
|
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(REGEX REPLACE ".*/([^/]*)\.cpp" "\\1" CORE_NAME_LC ${F})
|
||||||
string(TOUPPER ${CORE_NAME_LC} CORE_NAME)
|
string(TOUPPER ${CORE_NAME_LC} CORE_NAME)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
#if(WITH_LLVM)
|
# if(WITH_LLVM)
|
||||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_LLVM)
|
# target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_LLVM)
|
||||||
# #target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs})
|
# #target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs})
|
||||||
#endif()
|
# endif()
|
||||||
#if(WITH_TCC)
|
# if(WITH_TCC)
|
||||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_TCC)
|
# target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_TCC)
|
||||||
#endif()
|
# endif()
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc fmt::fmt)
|
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc fmt::fmt)
|
||||||
|
|
||||||
if(TARGET Boost::program_options)
|
if(TARGET Boost::program_options)
|
||||||
@ -162,8 +179,10 @@ if(TARGET Boost::program_options)
|
|||||||
else()
|
else()
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${BOOST_program_options_LIBRARY})
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${BOOST_program_options_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS})
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS})
|
||||||
if (Tcmalloc_FOUND)
|
|
||||||
|
if(Tcmalloc_FOUND)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${Tcmalloc_LIBRARIES})
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${Tcmalloc_LIBRARIES})
|
||||||
endif(Tcmalloc_FOUND)
|
endif(Tcmalloc_FOUND)
|
||||||
|
|
||||||
@ -181,22 +200,26 @@ if(BUILD_TESTING)
|
|||||||
# ... CMake code to create tests ...
|
# ... CMake code to create tests ...
|
||||||
add_test(NAME tgc-sim-interp
|
add_test(NAME tgc-sim-interp
|
||||||
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend interp)
|
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend interp)
|
||||||
|
|
||||||
if(WITH_TCC)
|
if(WITH_TCC)
|
||||||
add_test(NAME tgc-sim-tcc
|
add_test(NAME tgc-sim-tcc
|
||||||
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend tcc)
|
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend tcc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_LLVM)
|
if(WITH_LLVM)
|
||||||
add_test(NAME tgc-sim-llvm
|
add_test(NAME tgc-sim-llvm
|
||||||
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend llvm)
|
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend llvm)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_ASMJIT)
|
if(WITH_ASMJIT)
|
||||||
add_test(NAME tgc-sim-asmjit
|
add_test(NAME tgc-sim-asmjit
|
||||||
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend asmjit)
|
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend asmjit)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
###############################################################################
|
|
||||||
|
# ##############################################################################
|
||||||
#
|
#
|
||||||
###############################################################################
|
# ##############################################################################
|
||||||
if(TARGET scc-sysc)
|
if(TARGET scc-sysc)
|
||||||
project(dbt-rise-tgc_sc VERSION 1.0.0)
|
project(dbt-rise-tgc_sc VERSION 1.0.0)
|
||||||
set(LIB_SOURCES
|
set(LIB_SOURCES
|
||||||
@ -208,18 +231,20 @@ if(TARGET scc-sysc)
|
|||||||
add_library(${PROJECT_NAME} ${LIB_SOURCES})
|
add_library(${PROJECT_NAME} ${LIB_SOURCES})
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_SYSTEMC)
|
target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_SYSTEMC)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
||||||
|
|
||||||
foreach(F IN LISTS TGC_SOURCES)
|
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(REGEX REPLACE ".*/([^/]*)\.cpp" "\\1" CORE_NAME_LC ${F})
|
||||||
string(TOUPPER ${CORE_NAME_LC} CORE_NAME)
|
string(TOUPPER ${CORE_NAME_LC} CORE_NAME)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
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(LIB_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/sysc/core_complex.h)
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
@ -236,4 +261,3 @@ if(TARGET scc-sysc)
|
|||||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ Zifencei:
|
|||||||
size: 32
|
size: 32
|
||||||
branch: false
|
branch: false
|
||||||
delay: 1
|
delay: 1
|
||||||
RV32M:
|
RVM:
|
||||||
MUL:
|
MUL:
|
||||||
index: 49
|
index: 49
|
||||||
encoding: 0b00000010000000000000000000110011
|
encoding: 0b00000010000000000000000000110011
|
||||||
|
@ -109,7 +109,7 @@ template <> struct traits<${coreDef.name.toLowerCase()}> {
|
|||||||
|
|
||||||
enum sreg_flag_e { FLAGS };
|
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 -> %>
|
enum class opcode_e {<%instructions.eachWithIndex{instr, index -> %>
|
||||||
${instr.instruction.name} = ${index},<%}%>
|
${instr.instruction.name} = ${index},<%}%>
|
||||||
|
@ -79,21 +79,36 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
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 this_class = vm_impl<ARCH>;
|
||||||
using compile_func = continuation_e (this_class::*)(virt_addr_t&, code_word_t, jit_holder&);
|
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;
|
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);}
|
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>
|
template<unsigned W, typename U, typename S = typename std::make_signed<U>::type>
|
||||||
inline S sext(U from) {
|
inline S sext(U from) {
|
||||||
auto mask = (1ULL<<W) - 1;
|
auto mask = (1ULL<<W) - 1;
|
||||||
auto sign_mask = 1ULL<<(W-1);
|
auto sign_mask = 1ULL<<(W-1);
|
||||||
return (from & mask) | ((from & sign_mask) ? ~mask : 0);
|
return (from & mask) | ((from & sign_mask) ? ~mask : 0);
|
||||||
}
|
}
|
||||||
#include <vm/asmjit/helper_func.h>
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* start opcode definitions
|
* start opcode definitions
|
||||||
@ -127,16 +142,26 @@ private:
|
|||||||
<%instr.fields.eachLine{%>${it}
|
<%instr.fields.eachLine{%>${it}
|
||||||
<%}%>if(this->disass_enabled){
|
<%}%>if(this->disass_enabled){
|
||||||
/* generate disass */
|
/* 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, FuncSignatureT<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;
|
x86::Compiler& cc = jh.cc;
|
||||||
//ideally only do this if necessary (someone / plugin needs it)
|
cc.comment(fmt::format("${instr.name}_{:#x}:",pc.val).c_str());
|
||||||
cc.mov(jh.pc,PC);
|
|
||||||
cc.comment(fmt::format("\\n${instr.name}_{:#x}:",pc.val).c_str());
|
|
||||||
this->gen_sync(jh, PRE_SYNC, ${idx});
|
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);
|
gen_instr_prologue(jh);
|
||||||
cc.comment("\\n//behavior:");
|
cc.comment("//behavior:");
|
||||||
/*generate behavior*/
|
/*generate behavior*/
|
||||||
<%instr.behavior.eachLine{%>${it}
|
<%instr.behavior.eachLine{%>${it}
|
||||||
<%}%>
|
<%}%>
|
||||||
@ -149,9 +174,17 @@ private:
|
|||||||
* end opcode definitions
|
* end opcode definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
continuation_e illegal_intruction(virt_addr_t &pc, code_word_t instr, jit_holder& jh ) {
|
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;
|
return BRANCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
//decoding functionality
|
//decoding functionality
|
||||||
|
|
||||||
void populate_decoding_tree(decoding_tree_node* root){
|
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> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
|
||||||
|
|
||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
@ -224,8 +252,7 @@ vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
continuation_e
|
continuation_e vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, jit_holder& jh) {
|
||||||
vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, jit_holder& jh) {
|
|
||||||
enum {TRAP_ID=1<<16};
|
enum {TRAP_ID=1<<16};
|
||||||
code_word_t instr = 0;
|
code_word_t instr = 0;
|
||||||
phys_addr_t paddr(pc);
|
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;
|
f = &this_class::illegal_intruction;
|
||||||
return (this->*f)(pc, instr, jh);
|
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, 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_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 <>
|
template <>
|
||||||
std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreDef.name.toLowerCase()} *core, unsigned short port, bool dump) {
|
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 asmjit
|
||||||
} // namespace iss
|
} // namespace iss
|
||||||
|
|
||||||
#include <iss/factory.h>
|
|
||||||
#include <iss/arch/riscv_hart_m_p.h>
|
#include <iss/arch/riscv_hart_m_p.h>
|
||||||
#include <iss/arch/riscv_hart_mu_p.h>
|
#include <iss/arch/riscv_hart_mu_p.h>
|
||||||
|
#include <iss/factory.h>
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace {
|
namespace {
|
||||||
volatile std::array<bool, 2> dummy = {
|
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* 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 (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}};
|
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* 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 (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}};
|
return {cpu_ptr{cpu}, vm_ptr{vm}};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -357,22 +357,30 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD
|
|||||||
} // namespace interp
|
} // namespace interp
|
||||||
} // namespace iss
|
} // namespace iss
|
||||||
|
|
||||||
#include <iss/factory.h>
|
|
||||||
#include <iss/arch/riscv_hart_m_p.h>
|
#include <iss/arch/riscv_hart_m_p.h>
|
||||||
#include <iss/arch/riscv_hart_mu_p.h>
|
#include <iss/arch/riscv_hart_mu_p.h>
|
||||||
|
#include <iss/factory.h>
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace {
|
namespace {
|
||||||
volatile std::array<bool, 2> dummy = {
|
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* 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);
|
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 (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}};
|
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* 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);
|
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 (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}};
|
return {cpu_ptr{cpu}, vm_ptr{vm}};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -99,16 +99,11 @@ protected:
|
|||||||
std::tuple<continuation_e, BasicBlock *> gen_single_inst_behavior(virt_addr_t &, unsigned int &, BasicBlock *) override;
|
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_leave_behavior(BasicBlock *leave_blk) override;
|
||||||
|
|
||||||
void gen_raise_trap(uint16_t trap_id, uint16_t cause);
|
void gen_raise_trap(uint16_t trap_id, uint16_t cause);
|
||||||
|
|
||||||
void gen_leave_trap(unsigned lvl);
|
void gen_leave_trap(unsigned lvl);
|
||||||
|
|
||||||
void gen_wait(unsigned type);
|
void gen_wait(unsigned type);
|
||||||
|
|
||||||
void gen_trap_behavior(BasicBlock *) override;
|
void gen_trap_behavior(BasicBlock *) override;
|
||||||
|
void gen_instr_epilogue(BasicBlock *bb);
|
||||||
void gen_trap_check(BasicBlock *bb);
|
|
||||||
|
|
||||||
inline Value *gen_reg_load(unsigned i, unsigned level = 0) {
|
inline Value *gen_reg_load(unsigned i, unsigned level = 0) {
|
||||||
return this->builder.CreateLoad(this->get_typeptr(i), get_reg_ptr(i), false);
|
return this->builder.CreateLoad(this->get_typeptr(i), get_reg_ptr(i), false);
|
||||||
@ -162,20 +157,22 @@ private:
|
|||||||
/* instruction definitions */<%instructions.eachWithIndex{instr, idx -> %>
|
/* instruction definitions */<%instructions.eachWithIndex{instr, idx -> %>
|
||||||
/* instruction ${idx}: ${instr.name} */
|
/* instruction ${idx}: ${instr.name} */
|
||||||
std::tuple<continuation_e, BasicBlock*> __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr, BasicBlock* bb){
|
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;
|
uint64_t PC = pc.val;
|
||||||
<%instr.fields.eachLine{%>${it}
|
<%instr.fields.eachLine{%>${it}
|
||||||
<%}%>if(this->disass_enabled){
|
<%}%>if(this->disass_enabled){
|
||||||
/* generate console output when executing the command */<%instr.disass.eachLine{%>
|
/* generate console output when executing the command */<%instr.disass.eachLine{%>
|
||||||
${it}<%}%>
|
${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);
|
auto cur_pc_val = this->gen_const(32,pc.val);
|
||||||
pc=pc+ ${instr.length/8};
|
pc=pc+ ${instr.length/8};
|
||||||
this->gen_set_pc(pc, traits::NEXT_PC);
|
this->gen_set_pc(pc, traits::NEXT_PC);
|
||||||
|
|
||||||
|
/*generate behavior*/
|
||||||
<%instr.behavior.eachLine{%>${it}
|
<%instr.behavior.eachLine{%>${it}
|
||||||
<%}%>
|
<%}%>
|
||||||
this->gen_trap_check(bb);
|
this->gen_instr_epilogue(bb);
|
||||||
this->gen_sync(POST_SYNC, ${idx});
|
this->gen_sync(POST_SYNC, ${idx});
|
||||||
this->builder.CreateBr(bb);
|
this->builder.CreateBr(bb);
|
||||||
return returnValue;
|
return returnValue;
|
||||||
@ -195,7 +192,7 @@ private:
|
|||||||
pc = pc + ((instr & 3) == 3 ? 4 : 2);
|
pc = pc + ((instr & 3) == 3 ? 4 : 2);
|
||||||
this->gen_raise_trap(0, 2); // illegal instruction trap
|
this->gen_raise_trap(0, 2); // illegal instruction trap
|
||||||
this->gen_sync(iss::POST_SYNC, instr_descr.size());
|
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);
|
return std::make_tuple(BRANCH, nullptr);
|
||||||
}
|
}
|
||||||
//decoding functionality
|
//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);
|
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.SetInsertPoint(leave_blk);
|
||||||
this->builder.CreateRet(this->builder.CreateLoad(this->get_typeptr(traits::NEXT_PC),get_reg_ptr(traits::NEXT_PC), false));
|
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);
|
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(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);
|
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)) };
|
std::vector<Value *> args{ this->core_ptr, ConstantInt::get(getContext(), APInt(64, lvl)) };
|
||||||
this->builder.CreateCall(this->mod->getFunction("leave_trap"), args);
|
this->builder.CreateCall(this->mod->getFunction("leave_trap"), args);
|
||||||
auto *PC_val = this->gen_read_mem(traits::CSR, (lvl << 8) + 0x41, traits::XLEN / 8);
|
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);
|
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)) };
|
std::vector<Value *> args{ this->core_ptr, ConstantInt::get(getContext(), APInt(64, type)) };
|
||||||
this->builder.CreateCall(this->mod->getFunction("wait"), args);
|
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->builder.SetInsertPoint(trap_blk);
|
||||||
this->gen_sync(POST_SYNC, -1); //TODO get right InstrId
|
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);
|
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);
|
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* 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);
|
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(
|
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 llvm
|
||||||
} // namespace iss
|
} // namespace iss
|
||||||
|
|
||||||
#include <iss/factory.h>
|
|
||||||
#include <iss/arch/riscv_hart_m_p.h>
|
#include <iss/arch/riscv_hart_m_p.h>
|
||||||
#include <iss/arch/riscv_hart_mu_p.h>
|
#include <iss/arch/riscv_hart_mu_p.h>
|
||||||
|
#include <iss/factory.h>
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace {
|
namespace {
|
||||||
volatile std::array<bool, 2> dummy = {
|
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* 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 (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}};
|
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* 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 (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}};
|
return {cpu_ptr{cpu}, vm_ptr{vm}};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -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) {
|
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(" *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) {
|
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
|
} // namesapce tcc
|
||||||
} // namespace iss
|
} // namespace iss
|
||||||
|
|
||||||
#include <iss/factory.h>
|
|
||||||
#include <iss/arch/riscv_hart_m_p.h>
|
#include <iss/arch/riscv_hart_m_p.h>
|
||||||
#include <iss/arch/riscv_hart_mu_p.h>
|
#include <iss/arch/riscv_hart_mu_p.h>
|
||||||
|
#include <iss/factory.h>
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace {
|
namespace {
|
||||||
volatile std::array<bool, 2> dummy = {
|
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* 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);
|
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 (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}};
|
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* 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);
|
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 (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}};
|
return {cpu_ptr{cpu}, vm_ptr{vm}};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -327,7 +327,7 @@ set(OTHERS
|
|||||||
|
|
||||||
set(LIB_SOURCES ${PRIMITIVES} ${SPECIALIZE} ${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)
|
set_property(TARGET softfloat PROPERTY C_STANDARD 99)
|
||||||
target_compile_definitions(softfloat PRIVATE
|
target_compile_definitions(softfloat PRIVATE
|
||||||
SOFTFLOAT_ROUND_ODD
|
SOFTFLOAT_ROUND_ODD
|
||||||
@ -347,7 +347,7 @@ set_target_properties(softfloat PROPERTIES
|
|||||||
|
|
||||||
install(TARGETS softfloat
|
install(TARGETS softfloat
|
||||||
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
|
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
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libs # shared lib
|
||||||
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libs # for mac
|
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)
|
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
|
#ifndef _RISCV_HART_COMMON
|
||||||
#define _RISCV_HART_COMMON
|
#define _RISCV_HART_COMMON
|
||||||
|
|
||||||
#include "iss/arch_if.h"
|
|
||||||
#include <cstdint>
|
#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 iss {
|
||||||
namespace arch {
|
namespace arch {
|
||||||
@ -296,6 +302,61 @@ inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const
|
|||||||
break;
|
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 arch
|
||||||
} // namespace iss
|
} // namespace iss
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "iss/log_categories.h"
|
#include "iss/log_categories.h"
|
||||||
#include "iss/vm_if.h"
|
#include "iss/vm_if.h"
|
||||||
#include "riscv_hart_common.h"
|
#include "riscv_hart_common.h"
|
||||||
|
#include <stdexcept>
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
#define FMT_HEADER_ONLY
|
#define FMT_HEADER_ONLY
|
||||||
#endif
|
#endif
|
||||||
@ -55,6 +56,8 @@
|
|||||||
#include <util/ities.h>
|
#include <util/ities.h>
|
||||||
#include <util/sparse_array.h>
|
#include <util/sparse_array.h>
|
||||||
|
|
||||||
|
#include <iss/semihosting/semihosting.h>
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
#define likely(x) __builtin_expect(!!(x), 1)
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
@ -66,7 +69,7 @@
|
|||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
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:
|
protected:
|
||||||
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
||||||
const std::array<const char*, 16> trap_str = {{""
|
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_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:
|
protected:
|
||||||
struct riscv_instrumentation_if : public iss::instrumentation_if {
|
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]; }
|
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;
|
riscv_hart_m_p<BASE, FEAT>& arch;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -344,9 +351,11 @@ protected:
|
|||||||
reg_t fault_data;
|
reg_t fault_data;
|
||||||
uint64_t tohost = tohost_dflt;
|
uint64_t tohost = tohost_dflt;
|
||||||
uint64_t fromhost = fromhost_dflt;
|
uint64_t fromhost = fromhost_dflt;
|
||||||
unsigned to_host_wr_cnt = 0;
|
bool tohost_lower_written = false;
|
||||||
riscv_instrumentation_if instr_if;
|
riscv_instrumentation_if instr_if;
|
||||||
|
|
||||||
|
semihosting_cb_t<reg_t> semihosting_cb;
|
||||||
|
|
||||||
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
|
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_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>;
|
||||||
using csr_page_type = typename csr_type::page_type;
|
using csr_page_type = typename csr_type::page_type;
|
||||||
@ -419,6 +428,7 @@ protected:
|
|||||||
feature_config cfg;
|
feature_config cfg;
|
||||||
unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast<unsigned>(traits<BASE>::CLIC_NUM_IRQ)) : 16U};
|
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; }
|
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::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::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};
|
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) {
|
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");
|
FILE* fp = fopen(name.c_str(), "r");
|
||||||
if(fp) {
|
if(fp) {
|
||||||
std::array<char, 5> buf;
|
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,
|
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));
|
pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
||||||
if(res != iss::Ok)
|
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) {
|
for(const auto sec : reader.sections) {
|
||||||
if(sec->get_name() == ".symtab") {
|
if(sec->get_name() == ".tohost") {
|
||||||
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") {
|
|
||||||
tohost = sec->get_address();
|
tohost = sec->get_address();
|
||||||
fromhost = tohost + 0x40;
|
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) {
|
const unsigned length, uint8_t* const data) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if(access && iss::access_type::DEBUG) {
|
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) {
|
} 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 {
|
} else {
|
||||||
LOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
|
CPPLOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
try {
|
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 " : "";
|
const char* prefix = (access && iss::access_type::DEBUG) ? "debug " : "";
|
||||||
switch(length) {
|
switch(length) {
|
||||||
case 8:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 4:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 2:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 1:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr;
|
CPPLOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
try {
|
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);
|
res = write_mem(phys_addr, length, data);
|
||||||
}
|
}
|
||||||
if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) {
|
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;
|
fault_data = addr;
|
||||||
}
|
}
|
||||||
return res;
|
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
|
case 0x10023000: // UART1 base, TXFIFO reg
|
||||||
uart_buf << (char)data[0];
|
uart_buf << (char)data[0];
|
||||||
if(((char)data[0]) == '\n' || data[0] == 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()<<"'";
|
// '"<<uart_buf.str()<<"'";
|
||||||
std::cout << uart_buf.str();
|
std::cout << uart_buf.str();
|
||||||
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>
|
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) {
|
iss::status riscv_hart_m_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) {
|
||||||
switch(paddr.val) {
|
switch(paddr.val) {
|
||||||
|
// TODO remove UART, Peripherals should not be part of the ISS
|
||||||
case 0xFFFF0000: // UART0 base, TXFIFO reg
|
case 0xFFFF0000: // UART0 base, TXFIFO reg
|
||||||
if(((char)data[0]) == '\n' || data[0] == 0) {
|
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("");
|
uart_buf.str("");
|
||||||
} else if(((char)data[0]) != '\r')
|
} else if(((char)data[0]) != '\r')
|
||||||
uart_buf << (char)data[0];
|
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);
|
auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
|
||||||
if(tohost_lower || tohost_upper) {
|
if(tohost_lower || tohost_upper) {
|
||||||
uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask));
|
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) {
|
switch(hostvar >> 48) {
|
||||||
case 0:
|
case 0:
|
||||||
if(hostvar != 0x1) {
|
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";
|
<< "), stopping simulation";
|
||||||
} else {
|
} 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";
|
<< "), stopping simulation";
|
||||||
}
|
}
|
||||||
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
||||||
this->interrupt_sim = hostvar;
|
this->interrupt_sim = hostvar;
|
||||||
#ifndef WITH_TCC
|
|
||||||
throw(iss::simulation_stopped(hostvar));
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case 0x0101: {
|
case 0x0101: {
|
||||||
char c = static_cast<char>(hostvar & 0xff);
|
char c = static_cast<char>(hostvar & 0xff);
|
||||||
if(c == '\n' || c == 0) {
|
if(c == '\n' || c == 0) {
|
||||||
LOG(INFO) << "tohost send '" << uart_buf.str() << "'";
|
CPPLOG(INFO) << "tohost send '" << uart_buf.str() << "'";
|
||||||
uart_buf.str("");
|
uart_buf.str("");
|
||||||
} else
|
} else
|
||||||
uart_buf << c;
|
uart_buf << c;
|
||||||
to_host_wr_cnt = 0;
|
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
tohost_lower_written = false;
|
||||||
} else if(tohost_lower)
|
} 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)) {
|
} 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));
|
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;
|
*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 {
|
} else {
|
||||||
csr[mtval] = addr;
|
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;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
case 6:
|
case 6:
|
||||||
|
@ -55,6 +55,8 @@
|
|||||||
#include <util/ities.h>
|
#include <util/ities.h>
|
||||||
#include <util/sparse_array.h>
|
#include <util/sparse_array.h>
|
||||||
|
|
||||||
|
#include <iss/semihosting/semihosting.h>
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
#define likely(x) __builtin_expect(!!(x), 1)
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
@ -66,7 +68,7 @@
|
|||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
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:
|
protected:
|
||||||
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
||||||
const std::array<const char*, 16> trap_str = {{""
|
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_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:
|
protected:
|
||||||
struct riscv_instrumentation_if : public iss::instrumentation_if {
|
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]; }
|
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;
|
riscv_hart_msu_vp<BASE>& arch;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -395,9 +401,11 @@ protected:
|
|||||||
std::array<vm_info, 2> vm;
|
std::array<vm_info, 2> vm;
|
||||||
uint64_t tohost = tohost_dflt;
|
uint64_t tohost = tohost_dflt;
|
||||||
uint64_t fromhost = fromhost_dflt;
|
uint64_t fromhost = fromhost_dflt;
|
||||||
unsigned to_host_wr_cnt = 0;
|
bool tohost_lower_written = false;
|
||||||
riscv_instrumentation_if instr_if;
|
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 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_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>;
|
||||||
using csr_page_type = typename csr_type::page_type;
|
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,
|
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));
|
pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
||||||
if(res != iss::Ok)
|
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) {
|
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) {
|
const unsigned length, uint8_t* const data) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if(access && iss::access_type::DEBUG) {
|
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) {
|
} 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 {
|
} else {
|
||||||
LOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
|
CPPLOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
try {
|
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 " : "";
|
const char* prefix = (access && iss::access_type::DEBUG) ? "debug " : "";
|
||||||
switch(length) {
|
switch(length) {
|
||||||
case 8:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 4:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 2:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 1:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr;
|
CPPLOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
try {
|
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
|
case 0x10023000: // UART1 base, TXFIFO reg
|
||||||
uart_buf << (char)data[0];
|
uart_buf << (char)data[0];
|
||||||
if(((char)data[0]) == '\n' || data[0] == 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()<<"'";
|
// '"<<uart_buf.str()<<"'";
|
||||||
std::cout << uart_buf.str();
|
std::cout << uart_buf.str();
|
||||||
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) {
|
switch(paddr.val) {
|
||||||
case 0xFFFF0000: // UART0 base, TXFIFO reg
|
case 0xFFFF0000: // UART0 base, TXFIFO reg
|
||||||
if(((char)data[0]) == '\n' || data[0] == 0) {
|
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("");
|
uart_buf.str("");
|
||||||
} else if(((char)data[0]) != '\r')
|
} else if(((char)data[0]) != '\r')
|
||||||
uart_buf << (char)data[0];
|
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);
|
auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
|
||||||
if(tohost_lower || tohost_upper) {
|
if(tohost_lower || tohost_upper) {
|
||||||
uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask));
|
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) {
|
switch(hostvar >> 48) {
|
||||||
case 0:
|
case 0:
|
||||||
if(hostvar != 0x1) {
|
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";
|
<< "), stopping simulation";
|
||||||
} else {
|
} 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";
|
<< "), stopping simulation";
|
||||||
}
|
}
|
||||||
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
||||||
this->interrupt_sim = hostvar;
|
this->interrupt_sim = hostvar;
|
||||||
|
#ifndef WITH_TCC
|
||||||
|
throw(iss::simulation_stopped(hostvar));
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
// throw(iss::simulation_stopped(hostvar));
|
|
||||||
case 0x0101: {
|
case 0x0101: {
|
||||||
char c = static_cast<char>(hostvar & 0xff);
|
char c = static_cast<char>(hostvar & 0xff);
|
||||||
if(c == '\n' || c == 0) {
|
if(c == '\n' || c == 0) {
|
||||||
LOG(INFO) << "tohost send '" << uart_buf.str() << "'";
|
CPPLOG(INFO) << "tohost send '" << uart_buf.str() << "'";
|
||||||
uart_buf.str("");
|
uart_buf.str("");
|
||||||
} else
|
} else
|
||||||
uart_buf << c;
|
uart_buf << c;
|
||||||
to_host_wr_cnt = 0;
|
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
tohost_lower_written = false;
|
||||||
} else if(tohost_lower)
|
} 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)) {
|
} 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));
|
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;
|
*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[dpc] = addr;
|
||||||
// csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1<<6) | PRIV_M; //FIXME: cause should not be 4 (stepi)
|
// csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1<<6) | PRIV_M; //FIXME: cause should not be 4 (stepi)
|
||||||
csr[utval | (new_priv << 8)] = addr;
|
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;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
case 6:
|
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;
|
this->reg.pending_trap = 0;
|
||||||
}
|
}
|
||||||
size_t adr = ucause | (new_priv << 8);
|
size_t adr = ucause | (new_priv << 8);
|
||||||
csr[adr] = (trap_id << 31) + cause;
|
csr[adr] = (trap_id << (traits<BASE>::XLEN - 1)) + cause;
|
||||||
// update mstatus
|
// update mstatus
|
||||||
// xPP field of mstatus is written with the active privilege mode at the time
|
// xPP field of mstatus is written with the active privilege mode at the time
|
||||||
// of the trap; the x PIE field of mstatus
|
// of the trap; the x PIE field of mstatus
|
||||||
|
@ -55,6 +55,8 @@
|
|||||||
#include <util/ities.h>
|
#include <util/ities.h>
|
||||||
#include <util/sparse_array.h>
|
#include <util/sparse_array.h>
|
||||||
|
|
||||||
|
#include <iss/semihosting/semihosting.h>
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
#define likely(x) __builtin_expect(!!(x), 1)
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
@ -66,7 +68,7 @@
|
|||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
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:
|
protected:
|
||||||
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
||||||
const std::array<const char*, 16> trap_str = {{""
|
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_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:
|
protected:
|
||||||
struct riscv_instrumentation_if : public iss::instrumentation_if {
|
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]; }
|
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;
|
riscv_hart_mu_p<BASE, FEAT>& arch;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -371,9 +377,11 @@ protected:
|
|||||||
reg_t fault_data;
|
reg_t fault_data;
|
||||||
uint64_t tohost = tohost_dflt;
|
uint64_t tohost = tohost_dflt;
|
||||||
uint64_t fromhost = fromhost_dflt;
|
uint64_t fromhost = fromhost_dflt;
|
||||||
unsigned to_host_wr_cnt = 0;
|
bool tohost_lower_written = false;
|
||||||
riscv_instrumentation_if instr_if;
|
riscv_instrumentation_if instr_if;
|
||||||
|
|
||||||
|
semihosting_cb_t<reg_t> semihosting_cb;
|
||||||
|
|
||||||
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
|
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_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>;
|
||||||
using csr_page_type = typename csr_type::page_type;
|
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,
|
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));
|
pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
||||||
if(res != iss::Ok)
|
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) {
|
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) {
|
const unsigned length, uint8_t* const data) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if(access && iss::access_type::DEBUG) {
|
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)) {
|
} 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 {
|
} else {
|
||||||
LOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
|
CPPLOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
try {
|
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 " : "";
|
const char* prefix = (access && iss::access_type::DEBUG) ? "debug " : "";
|
||||||
switch(length) {
|
switch(length) {
|
||||||
case 8:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 4:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 2:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
case 1:
|
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;
|
<< std::hex << addr;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr;
|
CPPLOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
try {
|
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
|
case 0x10023000: // UART1 base, TXFIFO reg
|
||||||
uart_buf << (char)data[0];
|
uart_buf << (char)data[0];
|
||||||
if(((char)data[0]) == '\n' || data[0] == 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()<<"'";
|
// '"<<uart_buf.str()<<"'";
|
||||||
std::cout << uart_buf.str();
|
std::cout << uart_buf.str();
|
||||||
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>
|
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) {
|
iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) {
|
||||||
switch(paddr.val) {
|
switch(paddr.val) {
|
||||||
|
// TODO remove UART, Peripherals should not be part of the ISS
|
||||||
case 0xFFFF0000: // UART0 base, TXFIFO reg
|
case 0xFFFF0000: // UART0 base, TXFIFO reg
|
||||||
if(((char)data[0]) == '\n' || data[0] == 0) {
|
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("");
|
uart_buf.str("");
|
||||||
} else if(((char)data[0]) != '\r')
|
} else if(((char)data[0]) != '\r')
|
||||||
uart_buf << (char)data[0];
|
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);
|
auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
|
||||||
if(tohost_lower || tohost_upper) {
|
if(tohost_lower || tohost_upper) {
|
||||||
uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask));
|
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) {
|
switch(hostvar >> 48) {
|
||||||
case 0:
|
case 0:
|
||||||
if(hostvar != 0x1) {
|
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";
|
<< "), stopping simulation";
|
||||||
} else {
|
} 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";
|
<< "), stopping simulation";
|
||||||
}
|
}
|
||||||
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
||||||
this->interrupt_sim = hostvar;
|
this->interrupt_sim = hostvar;
|
||||||
|
#ifndef WITH_TCC
|
||||||
|
throw(iss::simulation_stopped(hostvar));
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
// throw(iss::simulation_stopped(hostvar));
|
|
||||||
case 0x0101: {
|
case 0x0101: {
|
||||||
char c = static_cast<char>(hostvar & 0xff);
|
char c = static_cast<char>(hostvar & 0xff);
|
||||||
if(c == '\n' || c == 0) {
|
if(c == '\n' || c == 0) {
|
||||||
LOG(INFO) << "tohost send '" << uart_buf.str() << "'";
|
CPPLOG(INFO) << "tohost send '" << uart_buf.str() << "'";
|
||||||
uart_buf.str("");
|
uart_buf.str("");
|
||||||
} else
|
} else
|
||||||
uart_buf << c;
|
uart_buf << c;
|
||||||
to_host_wr_cnt = 0;
|
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
tohost_lower_written = false;
|
||||||
} else if(tohost_lower)
|
} 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)) {
|
} 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));
|
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;
|
*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 {
|
} else {
|
||||||
csr[utval | (new_priv << 8)] = addr;
|
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;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
case 6:
|
case 6:
|
||||||
|
@ -81,7 +81,7 @@ template <> struct traits<tgc5c> {
|
|||||||
|
|
||||||
enum sreg_flag_e { FLAGS };
|
enum sreg_flag_e { FLAGS };
|
||||||
|
|
||||||
enum mem_type_e { MEM, FENCE, RES, CSR };
|
enum mem_type_e { MEM, FENCE, RES, CSR, IMEM = MEM };
|
||||||
|
|
||||||
enum class opcode_e {
|
enum class opcode_e {
|
||||||
LUI = 0,
|
LUI = 0,
|
||||||
|
@ -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) {
|
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?:;
|
// return idx<0?:;
|
||||||
data.clear();
|
data.clear();
|
||||||
avail.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 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});
|
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);
|
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;
|
<< 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;
|
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});
|
auto saddr = map_addr({iss::access_type::FETCH, iss::address_type::PHYSICAL, 0, addr});
|
||||||
unsigned handle = target_adapter_base::bp_lut.getEntry(saddr.val);
|
unsigned handle = target_adapter_base::bp_lut.getEntry(saddr.val);
|
||||||
if(handle) {
|
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
|
// TODO: check length of addr range
|
||||||
target_adapter_base::bp_lut.removeEntry(handle);
|
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;
|
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;
|
return Err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if&
|
|||||||
try {
|
try {
|
||||||
auto root = YAML::LoadAll(is);
|
auto root = YAML::LoadAll(is);
|
||||||
if(root.size() != 1) {
|
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]) {
|
for(auto p : root[0]) {
|
||||||
auto isa_subset = p.first;
|
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) {
|
} 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;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG(ERR) << "Could not open input file " << config_file_name;
|
CPPLOG(ERR) << "Could not open input file " << config_file_name;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ iss::plugin::instruction_count::instruction_count(std::string config_file_name)
|
|||||||
try {
|
try {
|
||||||
auto root = YAML::LoadAll(is);
|
auto root = YAML::LoadAll(is);
|
||||||
if(root.size() != 1) {
|
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]) {
|
for(auto p : root[0]) {
|
||||||
auto isa_subset = p.first;
|
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());
|
rep_counts.resize(delays.size());
|
||||||
} catch(YAML::ParserException& e) {
|
} 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 {
|
} 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;
|
size_t idx = 0;
|
||||||
for(auto it : delays) {
|
for(auto it : delays) {
|
||||||
if(rep_counts[idx] > 0 && it.instr_name.find("__" != 0))
|
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++;
|
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 <array>
|
||||||
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iss/factory.h>
|
#include <iss/factory.h>
|
||||||
|
#include <iss/semihosting/semihosting.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "iss/arch/tgc_mapper.h"
|
#include "iss/arch/tgc_mapper.h"
|
||||||
@ -52,7 +56,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
/*
|
/*
|
||||||
* Define and parse the program options
|
* 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")
|
("elf,f", po::value<std::vector<std::string>>(), "ELF file(s) to load")
|
||||||
("mem,m", po::value<std::string>(), "the memory input file")
|
("mem,m", po::value<std::string>(), "the memory input file")
|
||||||
("plugin,p", po::value<std::vector<std::string>>(), "plugin to activate")
|
("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");
|
("isa", po::value<std::string>()->default_value("tgc5c"), "core or isa name to use for simulation, use '?' to get list");
|
||||||
// clang-format on
|
// clang-format on
|
||||||
auto parsed = po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
|
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
|
// instantiate the simulator
|
||||||
iss::vm_ptr vm{nullptr};
|
iss::vm_ptr vm{nullptr};
|
||||||
iss::cpu_ptr cpu{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>());
|
std::string isa_opt(clim["isa"].as<std::string>());
|
||||||
if(isa_opt.size() == 0 || isa_opt == "?") {
|
if(isa_opt.size() == 0 || isa_opt == "?") {
|
||||||
auto list = f.get_names();
|
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;
|
std::cout << "Available implementations (core|platform|backend):\n - " << util::join(list, "\n - ") << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
} else if(isa_opt.find('|') != std::string::npos) {
|
} 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 {
|
} else {
|
||||||
auto base_isa = isa_opt.substr(0, 5);
|
auto base_isa = isa_opt.substr(0, 5);
|
||||||
if(base_isa == "tgc5d" || base_isa == "tgc5e") {
|
if(base_isa == "tgc5d" || base_isa == "tgc5e") {
|
||||||
@ -131,14 +137,14 @@ int main(int argc, char* argv[]) {
|
|||||||
} else {
|
} else {
|
||||||
isa_opt += "|m_p|" + clim["backend"].as<std::string>();
|
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) {
|
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;
|
return 127;
|
||||||
}
|
}
|
||||||
if(!vm) {
|
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;
|
return 127;
|
||||||
}
|
}
|
||||||
if(clim.count("plugin")) {
|
if(clim.count("plugin")) {
|
||||||
@ -174,7 +180,7 @@ int main(int argc, char* argv[]) {
|
|||||||
} else
|
} else
|
||||||
#endif
|
#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;
|
return 127;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,12 +202,12 @@ int main(int argc, char* argv[]) {
|
|||||||
if(clim.count("elf"))
|
if(clim.count("elf"))
|
||||||
for(std::string input : clim["elf"].as<std::vector<std::string>>()) {
|
for(std::string input : clim["elf"].as<std::vector<std::string>>()) {
|
||||||
auto start_addr = vm->get_arch()->load_file(input);
|
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;
|
start_address = start_addr.first;
|
||||||
}
|
}
|
||||||
for(std::string input : args) {
|
for(std::string input : args) {
|
||||||
auto start_addr = vm->get_arch()->load_file(input); // treat remaining arguments as elf files
|
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;
|
start_address = start_addr.first;
|
||||||
}
|
}
|
||||||
if(clim.count("reset")) {
|
if(clim.count("reset")) {
|
||||||
@ -211,11 +217,42 @@ int main(int argc, char* argv[]) {
|
|||||||
vm->reset(start_address);
|
vm->reset(start_address);
|
||||||
auto cycles = clim["instructions"].as<uint64_t>();
|
auto cycles = clim["instructions"].as<uint64_t>();
|
||||||
res = vm->start(cycles, dump);
|
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) {
|
} 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;
|
res = 2;
|
||||||
}
|
}
|
||||||
// cleanup to let plugins report of needed
|
// cleanup to let plugins report if needed
|
||||||
for(auto* p : plugin_list) {
|
for(auto* p : plugin_list) {
|
||||||
delete p;
|
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
@ -429,7 +429,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(*PC + (int32_t)imm);
|
*(X+rd) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int32_t)imm ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -459,9 +459,9 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,13 +489,13 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint32_t addr_mask = (uint32_t)- 2;
|
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) {
|
if(new_pc % traits::INSTR_ALIGNMENT) {
|
||||||
raise(0, 0);
|
raise(0, 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(*PC + 4);
|
*(X+rd) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)(4 ));
|
||||||
}
|
}
|
||||||
*NEXT_PC = new_pc;
|
*NEXT_PC = new_pc;
|
||||||
this->core.reg.last_branch = 1;
|
this->core.reg.last_branch = 1;
|
||||||
@ -525,11 +525,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(*(X+rs1) == *(X+rs2)) {
|
if(*(X+rs1) == *(X+rs2)) {
|
||||||
if(imm % traits::INSTR_ALIGNMENT) {
|
if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) {
|
||||||
raise(0, 0);
|
raise(0, 0);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -558,11 +558,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(*(X+rs1) != *(X+rs2)) {
|
if(*(X+rs1) != *(X+rs2)) {
|
||||||
if(imm % traits::INSTR_ALIGNMENT) {
|
if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) {
|
||||||
raise(0, 0);
|
raise(0, 0);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -591,11 +591,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if((int32_t)*(X+rs1) < (int32_t)*(X+rs2)) {
|
if((int32_t)*(X+rs1) < (int32_t)*(X+rs2)) {
|
||||||
if(imm % traits::INSTR_ALIGNMENT) {
|
if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) {
|
||||||
raise(0, 0);
|
raise(0, 0);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -624,11 +624,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if((int32_t)*(X+rs1) >= (int32_t)*(X+rs2)) {
|
if((int32_t)*(X+rs1) >= (int32_t)*(X+rs2)) {
|
||||||
if(imm % traits::INSTR_ALIGNMENT) {
|
if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) {
|
||||||
raise(0, 0);
|
raise(0, 0);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,11 +657,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(*(X+rs1) < *(X+rs2)) {
|
if(*(X+rs1) < *(X+rs2)) {
|
||||||
if(imm % traits::INSTR_ALIGNMENT) {
|
if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) {
|
||||||
raise(0, 0);
|
raise(0, 0);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -690,11 +690,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(*(X+rs1) >= *(X+rs2)) {
|
if(*(X+rs1) >= *(X+rs2)) {
|
||||||
if(imm % traits::INSTR_ALIGNMENT) {
|
if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) {
|
||||||
raise(0, 0);
|
raise(0, 0);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -722,7 +722,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
int8_t res = (int8_t)res_27;
|
int8_t res = (int8_t)res_27;
|
||||||
@ -753,7 +753,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
int16_t res = (int16_t)res_28;
|
int16_t res = (int16_t)res_28;
|
||||||
@ -784,7 +784,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
int32_t res = (int32_t)res_29;
|
int32_t res = (int32_t)res_29;
|
||||||
@ -815,7 +815,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
uint8_t res = res_30;
|
uint8_t res = res_30;
|
||||||
@ -846,7 +846,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
uint16_t res = res_31;
|
uint16_t res = res_31;
|
||||||
@ -877,7 +877,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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));
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
}
|
}
|
||||||
@ -904,7 +904,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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));
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
}
|
}
|
||||||
@ -931,7 +931,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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));
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
}
|
}
|
||||||
@ -959,7 +959,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
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) ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1202,7 +1202,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(*(X+rs1) + *(X+rs2));
|
*(X+rd) = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)(*(X+rs2) ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1229,7 +1229,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(*(X+rs1) - *(X+rs2));
|
*(X+rd) = (uint32_t)((uint64_t)(*(X+rs1) ) - (uint64_t)(*(X+rs2) ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1256,7 +1256,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
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 )));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1364,7 +1364,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
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 )));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1391,7 +1391,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
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 ))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1772,7 +1772,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)res;
|
*(X+rd) = (uint32_t)res;
|
||||||
}
|
}
|
||||||
@ -1800,7 +1800,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(res >> traits::XLEN);
|
*(X+rd) = (uint32_t)(res >> traits::XLEN);
|
||||||
}
|
}
|
||||||
@ -1828,7 +1828,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(res >> traits::XLEN);
|
*(X+rd) = (uint32_t)(res >> traits::XLEN);
|
||||||
}
|
}
|
||||||
@ -1856,7 +1856,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(res >> traits::XLEN);
|
*(X+rd) = (uint32_t)(res >> traits::XLEN);
|
||||||
}
|
}
|
||||||
@ -1888,7 +1888,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
int32_t divisor = (int32_t)*(X+rs2);
|
int32_t divisor = (int32_t)*(X+rs2);
|
||||||
if(rd != 0) {
|
if(rd != 0) {
|
||||||
if(divisor != 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) {
|
if(*(X+rs1) == MMIN && divisor == - 1) {
|
||||||
*(X+rd) = MMIN;
|
*(X+rd) = MMIN;
|
||||||
}
|
}
|
||||||
@ -1926,7 +1926,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
else {
|
else {
|
||||||
if(*(X+rs2) != 0) {
|
if(*(X+rs2) != 0) {
|
||||||
if(rd != 0) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(*(X+rs1) / *(X+rs2));
|
*(X+rd) = *(X+rs1) / *(X+rs2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1959,7 +1959,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(*(X+rs2) != 0) {
|
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(*(X+rs1) == MMIN && (int32_t)*(X+rs2) == - 1) {
|
||||||
if(rd != 0) {
|
if(rd != 0) {
|
||||||
*(X+rd) = 0;
|
*(X+rd) = 0;
|
||||||
@ -1967,7 +1967,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2030,7 +2030,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
// execute instruction
|
// execute instruction
|
||||||
{
|
{
|
||||||
if(imm) {
|
if(imm) {
|
||||||
*(X+rd + 8) = (uint32_t)(*(X+2) + imm);
|
*(X+rd + 8) = (uint32_t)((uint64_t)(*(X+2) ) + (uint64_t)(imm ));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
@ -2054,7 +2054,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
*NEXT_PC = *PC + 2;
|
*NEXT_PC = *PC + 2;
|
||||||
// execute instruction
|
// 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);
|
int32_t res_38 = super::template read_mem<int32_t>(traits::MEM, offs);
|
||||||
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
*(X+rd + 8) = (uint32_t)(int32_t)res_38;
|
*(X+rd + 8) = (uint32_t)(int32_t)res_38;
|
||||||
@ -2077,7 +2077,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
*NEXT_PC = *PC + 2;
|
*NEXT_PC = *PC + 2;
|
||||||
// execute instruction
|
// 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));
|
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();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
}
|
}
|
||||||
@ -2103,7 +2103,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rs1 != 0) {
|
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) ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2136,8 +2136,8 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
*NEXT_PC = *PC + 2;
|
*NEXT_PC = *PC + 2;
|
||||||
// execute instruction
|
// execute instruction
|
||||||
{
|
{
|
||||||
*(X+1) = (uint32_t)(*PC + 2);
|
*(X+1) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)(2 ));
|
||||||
*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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2207,7 +2207,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
// execute instruction
|
// execute instruction
|
||||||
{
|
{
|
||||||
if(nzimm) {
|
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 {
|
else {
|
||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
@ -2289,7 +2289,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
*NEXT_PC = *PC + 2;
|
*NEXT_PC = *PC + 2;
|
||||||
// execute instruction
|
// 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;
|
break;
|
||||||
}// @suppress("No break at end of case")
|
}// @suppress("No break at end of case")
|
||||||
@ -2308,7 +2308,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
*NEXT_PC = *PC + 2;
|
*NEXT_PC = *PC + 2;
|
||||||
// execute instruction
|
// 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;
|
break;
|
||||||
}// @suppress("No break at end of case")
|
}// @suppress("No break at end of case")
|
||||||
@ -2382,7 +2382,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
*NEXT_PC = *PC + 2;
|
*NEXT_PC = *PC + 2;
|
||||||
// execute instruction
|
// 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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2403,7 +2403,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
// execute instruction
|
// execute instruction
|
||||||
{
|
{
|
||||||
if(*(X+rs1 + 8) == 0) {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2425,7 +2425,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
// execute instruction
|
// execute instruction
|
||||||
{
|
{
|
||||||
if(*(X+rs1 + 8) != 0) {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2476,7 +2476,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
int32_t res_39 = super::template read_mem<int32_t>(traits::MEM, offs);
|
||||||
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
*(X+rd) = (uint32_t)(int32_t)res_39;
|
*(X+rd) = (uint32_t)(int32_t)res_39;
|
||||||
@ -2525,7 +2525,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
// execute instruction
|
// execute instruction
|
||||||
{
|
{
|
||||||
if(rs1 && rs1 < traits::RFS) {
|
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;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2567,7 +2567,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(rd != 0) {
|
if(rd != 0) {
|
||||||
*(X+rd) = (uint32_t)(*(X+rd) + *(X+rs2));
|
*(X+rd) = (uint32_t)((uint64_t)(*(X+rd) ) + (uint64_t)(*(X+rs2) ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2592,8 +2592,8 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint32_t new_pc = *(X+rs1);
|
uint32_t new_pc = *(X+rs1);
|
||||||
*(X+1) = (uint32_t)(*PC + 2);
|
*(X+1) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)(2 ));
|
||||||
*NEXT_PC = new_pc & ~ 0x1;
|
*NEXT_PC = new_pc & (uint32_t)(~ 1 );
|
||||||
this->core.reg.last_branch = 1;
|
this->core.reg.last_branch = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2631,7 +2631,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
raise(0, 2);
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
else {
|
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));
|
super::template write_mem<uint32_t>(traits::MEM, offs, (uint32_t)*(X+rs2));
|
||||||
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception();
|
||||||
}
|
}
|
||||||
@ -2695,16 +2695,24 @@ std::unique_ptr<vm_if> create<arch::tgc5c>(arch::tgc5c *core, unsigned short por
|
|||||||
namespace iss {
|
namespace iss {
|
||||||
namespace {
|
namespace {
|
||||||
volatile std::array<bool, 2> dummy = {
|
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* cpu = new iss::arch::riscv_hart_m_p<iss::arch::tgc5c>();
|
||||||
auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false);
|
auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false);
|
||||||
if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
|
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}};
|
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* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::tgc5c>();
|
||||||
auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false);
|
auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false);
|
||||||
if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
|
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}};
|
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
Loading…
Reference in New Issue
Block a user