Merge branch 'develop' into main

This commit is contained in:
Eyck Jentzsch 2024-06-21 08:52:03 +02:00
commit eb99751ad9
24 changed files with 8336 additions and 6176 deletions

View File

@ -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)
@ -14,59 +15,69 @@ find_package(Boost COMPONENTS coroutine REQUIRED)
add_subdirectory(softfloat) add_subdirectory(softfloat)
set(LIB_SOURCES set(LIB_SOURCES
src/iss/plugin/instruction_count.cpp src/iss/plugin/instruction_count.cpp
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()
if(WITH_ASMJIT)
list(APPEND LIB_SOURCES
src/vm/asmjit/vm_tgc5c.cpp
) )
endif() endif()
if(WITH_ASMJIT)
list(APPEND LIB_SOURCES
src/vm/asmjit/vm_tgc5c.cpp
)
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)
FILE(GLOB LLVM_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/llvm/vm_*.cpp) FILE(GLOB LLVM_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/llvm/vm_*.cpp)
list(APPEND LIB_SOURCES ${LLVM_GEN_SOURCES}) list(APPEND LIB_SOURCES ${LLVM_GEN_SOURCES})
endif() endif()
if(WITH_TCC) 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,60 +86,67 @@ 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))
target_compile_definitions(${PROJECT_NAME} INTERFACE ${DBT_CORE_DEFS}) if(NOT(DBT_CORE_DEFS STREQUAL DBT_CORE_DEFS-NOTFOUND))
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)
endif() endif()
if(WITH_LLVM) 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)
target_link_libraries( ${PROJECT_NAME} PUBLIC ${LLVM_LIBRARIES}) if(BUILD_SHARED_LIBS)
endif() target_link_libraries(${PROJECT_NAME} PUBLIC ${LLVM_LIBRARIES})
endif()
endif() endif()
set_target_properties(${PROJECT_NAME} PROPERTIES set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
FRAMEWORK FALSE FRAMEWORK FALSE
) )
install(TARGETS ${PROJECT_NAME} COMPONENT ${PROJECT_NAME} install(TARGETS ${PROJECT_NAME} COMPONENT ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} # headers for mac (note the different component -> different package) PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} # headers for mac (note the different component -> different package)
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers
) )
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss COMPONENT ${PROJECT_NAME} 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,78 +179,85 @@ 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)
install(TARGETS tgc-sim install(TARGETS tgc-sim
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} # headers for mac (note the different component -> different package) PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} # headers for mac (note the different component -> different package)
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers
) )
if(BUILD_TESTING) 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)
add_test(NAME tgc-sim-tcc if(WITH_TCC)
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend tcc) add_test(NAME tgc-sim-tcc
endif() COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend tcc)
if(WITH_LLVM) endif()
add_test(NAME tgc-sim-llvm
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend llvm) if(WITH_LLVM)
endif() add_test(NAME tgc-sim-llvm
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend llvm)
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
src/sysc/core_complex.cpp src/sysc/core_complex.cpp
src/sysc/register_tgc_c.cpp src/sysc/register_tgc_c.cpp
) )
FILE(GLOB GEN_SC_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/sysc/register_*.cpp) FILE(GLOB GEN_SC_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/sysc/register_*.cpp)
list(APPEND LIB_SOURCES ${GEN_SC_SOURCES}) list(APPEND LIB_SOURCES ${GEN_SC_SOURCES})
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) target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc scc-sysc)
# if(WITH_LLVM)
# target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs}) # if(WITH_LLVM)
# endif() # 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}
FRAMEWORK FALSE FRAMEWORK FALSE
PUBLIC_HEADER "${LIB_HEADERS}" # specify the public headers PUBLIC_HEADER "${LIB_HEADERS}" # specify the public headers
) )
install(TARGETS ${PROJECT_NAME} COMPONENT ${PROJECT_NAME} install(TARGETS ${PROJECT_NAME} COMPONENT ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/sysc # headers for mac (note the different component -> different package) PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/sysc # headers for mac (note the different component -> different package)
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers
) )
endif() endif()

View File

@ -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

View File

@ -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},<%}%>

View File

@ -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};
gen_instr_prologue(jh, pc.val); cc.mov(jh.next_pc, pc.val);
cc.comment("\\n//behavior:");
gen_instr_prologue(jh);
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}};
}) })
}; };

View File

@ -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}};
}) })
}; };

View File

@ -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}};
}) })
}; };

View File

@ -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}};
}) })
}; };

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }

View File

@ -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++;
} }
} }

View 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>;

View 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

View File

@ -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;
} }

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff