Compare commits
40 Commits
feature/is
...
417076f8e6
Author | SHA1 | Date | |
---|---|---|---|
417076f8e6 | |||
70839bbbf2 | |||
8db0cc5d05 | |||
212fb1c8ff | |||
f74f98f361 | |||
f074092a78 | |||
633c0d21a0 | |||
51f6fbe0dd | |||
de45d06878 | |||
c7038cafa5 | |||
40f50b0ec0 | |||
b360fc2c75 | |||
e21f8dc379 | |||
8ee3ac90f7 | |||
d5763d2f36 | |||
b5d915f389 | |||
813b40409d | |||
c8a4a4c736 | |||
18e08cfc50 | |||
20e920338c | |||
e151416f58 | |||
24de2bbdf5 | |||
e68f9c573f | |||
f38cc7d8b9 | |||
7af7e040da | |||
6e52af168b | |||
bd0d15f3a2 | |||
c78026b720 | |||
edba497fa1 | |||
94e46b9968 | |||
9459632f6c | |||
a0ca3cdfa5 | |||
720236ec3f | |||
957145ca84 | |||
0b719a4b57 | |||
57da07eb17 | |||
b4b03f7850 | |||
145a0cf68b | |||
1cef7de8c7 | |||
e95f422aab |
171
CMakeLists.txt
171
CMakeLists.txt
@ -1,114 +1,109 @@
|
|||||||
cmake_minimum_required(VERSION 3.12)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
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)
|
||||||
|
include(flink)
|
||||||
|
|
||||||
find_package(elfio QUIET)
|
find_package(elfio QUIET)
|
||||||
find_package(Boost COMPONENTS coroutine)
|
|
||||||
find_package(jsoncpp)
|
find_package(jsoncpp)
|
||||||
|
find_package(Boost COMPONENTS coroutine REQUIRED)
|
||||||
if(WITH_LLVM)
|
|
||||||
if(DEFINED ENV{LLVM_HOME})
|
|
||||||
find_path (LLVM_DIR LLVM-Config.cmake $ENV{LLVM_HOME}/lib/cmake/llvm)
|
|
||||||
endif(DEFINED ENV{LLVM_HOME})
|
|
||||||
find_package(LLVM REQUIRED CONFIG)
|
|
||||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
|
||||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
|
||||||
llvm_map_components_to_libnames(llvm_libs support core mcjit x86codegen x86asmparser)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#Mac needed variables (adapt for your needs - http://www.cmake.org/Wiki/CMake_RPATH_handling#Mac_OS_X_and_the_RPATH)
|
|
||||||
#set(CMAKE_MACOSX_RPATH ON)
|
|
||||||
#set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
|
||||||
#set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
|
||||||
#set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
|
||||||
#set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
|
||||||
|
|
||||||
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/tgc_c.cpp
|
src/iss/arch/tgc5c.cpp
|
||||||
src/vm/interp/vm_tgc_c.cpp
|
src/vm/interp/vm_tgc5c.cpp
|
||||||
src/vm/fp_functions.cpp
|
src/vm/fp_functions.cpp
|
||||||
)
|
)
|
||||||
|
if(WITH_TCC)
|
||||||
|
list(APPEND LIB_SOURCES
|
||||||
|
src/vm/tcc/vm_tgc5c.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(WITH_LLVM)
|
||||||
|
list(APPEND LIB_SOURCES
|
||||||
|
src/vm/llvm/vm_tgc5c.cpp
|
||||||
|
src/vm/llvm/fp_impl.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# library files
|
# library files
|
||||||
if(TARGET ${CORE_NAME}_cpp)
|
FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp)
|
||||||
list(APPEND LIB_SOURCES ${${CORE_NAME}_OUTPUT_FILES})
|
FILE(GLOB GEN_VM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp)
|
||||||
else()
|
list(APPEND LIB_SOURCES ${GEN_ISS_SOURCES} ${GEN_VM_SOURCES})
|
||||||
FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp)
|
foreach(FILEPATH ${GEN_ISS_SOURCES})
|
||||||
FILE(GLOB GEN_VM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp)
|
get_filename_component(CORE ${FILEPATH} NAME_WE)
|
||||||
list(APPEND LIB_SOURCES ${GEN_ISS_SOURCES} ${GEN_VM_SOURCES})
|
string(TOUPPER ${CORE} CORE)
|
||||||
foreach(FILEPATH ${GEN_ISS_SOURCES})
|
list(APPEND LIB_DEFINES CORE_${CORE})
|
||||||
get_filename_component(CORE ${FILEPATH} NAME_WE)
|
endforeach()
|
||||||
string(TOUPPER ${CORE} CORE)
|
message(STATUS "Core defines are ${LIB_DEFINES}")
|
||||||
list(APPEND LIB_DEFINES CORE_${CORE})
|
|
||||||
endforeach()
|
|
||||||
message("Defines are ${LIB_DEFINES}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TARGET RapidJSON OR TARGET RapidJSON::RapidJSON)
|
|
||||||
list(APPEND LIB_SOURCES src/iss/plugin/cycle_estimate.cpp src/iss/plugin/pctrace.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WITH_LLVM)
|
if(WITH_LLVM)
|
||||||
FILE(GLOB LLVM_GEN_SOURCES
|
FILE(GLOB LLVM_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/llvm/vm_*.cpp)
|
||||||
${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
|
FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/tcc/vm_*.cpp)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/vm/tcc/vm_*.cpp
|
|
||||||
)
|
|
||||||
list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
|
list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Define the library
|
if(TARGET RapidJSON OR TARGET RapidJSON::RapidJSON)
|
||||||
add_library(${PROJECT_NAME} ${LIB_SOURCES})
|
list(APPEND LIB_SOURCES
|
||||||
# list code gen dependencies
|
src/iss/plugin/cycle_estimate.cpp
|
||||||
if(TARGET ${CORE_NAME}_cpp)
|
src/iss/plugin/pctrace.cpp
|
||||||
add_dependencies(${PROJECT_NAME} ${CORE_NAME}_cpp)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
if(TARGET jsoncpp::jsoncpp)
|
||||||
|
list(APPEND LIB_SOURCES
|
||||||
|
src/iss/plugin/instruction_count.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
# Define the library
|
||||||
|
add_library(${PROJECT_NAME} SHARED ${LIB_SOURCES})
|
||||||
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-shift-count-overflow)
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-shift-count-overflow)
|
||||||
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_link_libraries(${PROJECT_NAME} PUBLIC softfloat scc-util Boost::coroutine)
|
|
||||||
|
target_force_link_libraries(${PROJECT_NAME} PRIVATE dbt-rise-core)
|
||||||
|
# only re-export the include paths
|
||||||
|
get_target_property(DBT_CORE_INCL dbt-rise-core INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
target_include_directories(${PROJECT_NAME} INTERFACE ${DBT_CORE_INCL})
|
||||||
|
get_target_property(DBT_CORE_DEFS dbt-rise-core INTERFACE_COMPILE_DEFINITIONS)
|
||||||
|
if(NOT (DBT_CORE_DEFS STREQUAL DBT_CORE_DEFS-NOTFOUND))
|
||||||
|
target_compile_definitions(${PROJECT_NAME} INTERFACE ${DBT_CORE_DEFS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC elfio::elfio softfloat scc-util Boost::coroutine)
|
||||||
if(TARGET jsoncpp::jsoncpp)
|
if(TARGET jsoncpp::jsoncpp)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC jsoncpp::jsoncpp)
|
target_link_libraries(${PROJECT_NAME} PUBLIC jsoncpp::jsoncpp)
|
||||||
else()
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC jsoncpp)
|
|
||||||
endif()
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND BUILD_SHARED_LIBS)
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC -Wl,--whole-archive dbt-rise-core -Wl,--no-whole-archive)
|
|
||||||
else()
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-core)
|
|
||||||
endif()
|
|
||||||
if(TARGET elfio::elfio)
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC elfio::elfio)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "No elfio library found, maybe a find_package() call is missing")
|
|
||||||
endif()
|
endif()
|
||||||
if(TARGET lz4::lz4)
|
if(TARGET lz4::lz4)
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_LZ4)
|
target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_LZ4)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC lz4::lz4)
|
target_link_libraries(${PROJECT_NAME} PUBLIC lz4::lz4)
|
||||||
endif()
|
endif()
|
||||||
if(TARGET RapidJSON::RapidJSON)
|
if(TARGET RapidJSON)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC RapidJSON::RapidJSON)
|
|
||||||
elseif(TARGET RapidJSON)
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC RapidJSON)
|
target_link_libraries(${PROJECT_NAME} PUBLIC RapidJSON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WITH_LLVM)
|
||||||
|
find_package(LLVM)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PUBLIC ${LLVM_DEFINITIONS})
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC ${LLVM_INCLUDE_DIRS})
|
||||||
|
if(BUILD_SHARED_LIBS)
|
||||||
|
target_link_libraries( ${PROJECT_NAME} PUBLIC ${LLVM_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
@ -153,15 +148,16 @@ foreach(F IN LISTS TGC_SOURCES)
|
|||||||
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()
|
||||||
# Links the target exe against the libraries
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc)
|
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc fmt::fmt)
|
||||||
|
|
||||||
if(TARGET Boost::program_options)
|
if(TARGET Boost::program_options)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::program_options)
|
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::program_options)
|
||||||
else()
|
else()
|
||||||
@ -181,15 +177,32 @@ install(TARGETS tgc-sim
|
|||||||
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)
|
||||||
|
# ... CMake code to create tests ...
|
||||||
|
add_test(NAME tgc-sim-interp
|
||||||
|
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend interp)
|
||||||
|
if(WITH_TCC)
|
||||||
|
add_test(NAME tgc-sim-tcc
|
||||||
|
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend tcc)
|
||||||
|
endif()
|
||||||
|
if(WITH_LLVM)
|
||||||
|
add_test(NAME tgc-sim-llvm
|
||||||
|
COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend llvm)
|
||||||
|
endif()
|
||||||
|
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)
|
||||||
add_library(${PROJECT_NAME}
|
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)
|
||||||
|
list(APPEND LIB_SOURCES ${GEN_SC_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)
|
||||||
@ -200,9 +213,9 @@ if(TARGET scc-sysc)
|
|||||||
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)
|
# if(WITH_LLVM)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs})
|
# target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs})
|
||||||
endif()
|
# 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
|
||||||
|
@ -15,59 +15,51 @@ RV32I:
|
|||||||
- JAL:
|
- JAL:
|
||||||
encoding: 0b00000000000000000000000001101111
|
encoding: 0b00000000000000000000000001101111
|
||||||
mask: 0b00000000000000000000000001111111
|
mask: 0b00000000000000000000000001111111
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: true
|
branch: true
|
||||||
delay: 1
|
delay: 1
|
||||||
- JALR:
|
- JALR:
|
||||||
encoding: 0b00000000000000000000000001100111
|
encoding: 0b00000000000000000000000001100111
|
||||||
mask: 0b00000000000000000111000001111111
|
mask: 0b00000000000000000111000001111111
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: true
|
branch: true
|
||||||
delay: 1
|
delay: 1
|
||||||
- BEQ:
|
- BEQ:
|
||||||
encoding: 0b00000000000000000000000001100011
|
encoding: 0b00000000000000000000000001100011
|
||||||
mask: 0b00000000000000000111000001111111
|
mask: 0b00000000000000000111000001111111
|
||||||
attributes: [[name:no_cont], [name:cond]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: true
|
branch: true
|
||||||
delay: [1,1]
|
delay: 1
|
||||||
- BNE:
|
- BNE:
|
||||||
encoding: 0b00000000000000000001000001100011
|
encoding: 0b00000000000000000001000001100011
|
||||||
mask: 0b00000000000000000111000001111111
|
mask: 0b00000000000000000111000001111111
|
||||||
attributes: [[name:no_cont], [name:cond]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: true
|
branch: true
|
||||||
delay: [1,1]
|
delay: 1
|
||||||
- BLT:
|
- BLT:
|
||||||
encoding: 0b00000000000000000100000001100011
|
encoding: 0b00000000000000000100000001100011
|
||||||
mask: 0b00000000000000000111000001111111
|
mask: 0b00000000000000000111000001111111
|
||||||
attributes: [[name:no_cont], [name:cond]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: true
|
branch: true
|
||||||
delay: [1,1]
|
delay: 1
|
||||||
- BGE:
|
- BGE:
|
||||||
encoding: 0b00000000000000000101000001100011
|
encoding: 0b00000000000000000101000001100011
|
||||||
mask: 0b00000000000000000111000001111111
|
mask: 0b00000000000000000111000001111111
|
||||||
attributes: [[name:no_cont], [name:cond]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: true
|
branch: true
|
||||||
delay: [1,1]
|
delay: 1
|
||||||
- BLTU:
|
- BLTU:
|
||||||
encoding: 0b00000000000000000110000001100011
|
encoding: 0b00000000000000000110000001100011
|
||||||
mask: 0b00000000000000000111000001111111
|
mask: 0b00000000000000000111000001111111
|
||||||
attributes: [[name:no_cont], [name:cond]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: true
|
branch: true
|
||||||
delay: [1,1]
|
delay: 1
|
||||||
- BGEU:
|
- BGEU:
|
||||||
encoding: 0b00000000000000000111000001100011
|
encoding: 0b00000000000000000111000001100011
|
||||||
mask: 0b00000000000000000111000001111111
|
mask: 0b00000000000000000111000001111111
|
||||||
attributes: [[name:no_cont], [name:cond]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: true
|
branch: true
|
||||||
delay: [1,1]
|
delay: 1
|
||||||
- LB:
|
- LB:
|
||||||
encoding: 0b00000000000000000000000000000011
|
encoding: 0b00000000000000000000000000000011
|
||||||
mask: 0b00000000000000000111000001111111
|
mask: 0b00000000000000000111000001111111
|
||||||
@ -239,14 +231,12 @@ RV32I:
|
|||||||
- ECALL:
|
- ECALL:
|
||||||
encoding: 0b00000000000000000000000001110011
|
encoding: 0b00000000000000000000000001110011
|
||||||
mask: 0b11111111111111111111111111111111
|
mask: 0b11111111111111111111111111111111
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: false
|
branch: false
|
||||||
delay: 1
|
delay: 1
|
||||||
- EBREAK:
|
- EBREAK:
|
||||||
encoding: 0b00000000000100000000000001110011
|
encoding: 0b00000000000100000000000001110011
|
||||||
mask: 0b11111111111111111111111111111111
|
mask: 0b11111111111111111111111111111111
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 32
|
size: 32
|
||||||
branch: false
|
branch: false
|
||||||
delay: 1
|
delay: 1
|
||||||
@ -391,7 +381,6 @@ RV32IC:
|
|||||||
- CJAL:
|
- CJAL:
|
||||||
encoding: 0b0010000000000001
|
encoding: 0b0010000000000001
|
||||||
mask: 0b1110000000000011
|
mask: 0b1110000000000011
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 16
|
size: 16
|
||||||
branch: true
|
branch: true
|
||||||
delay: 1
|
delay: 1
|
||||||
@ -458,24 +447,21 @@ RV32IC:
|
|||||||
- CJ:
|
- CJ:
|
||||||
encoding: 0b1010000000000001
|
encoding: 0b1010000000000001
|
||||||
mask: 0b1110000000000011
|
mask: 0b1110000000000011
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 16
|
size: 16
|
||||||
branch: true
|
branch: true
|
||||||
delay: 1
|
delay: 1
|
||||||
- CBEQZ:
|
- CBEQZ:
|
||||||
encoding: 0b1100000000000001
|
encoding: 0b1100000000000001
|
||||||
mask: 0b1110000000000011
|
mask: 0b1110000000000011
|
||||||
attributes: [[name:no_cont], [name:cond]]
|
|
||||||
size: 16
|
size: 16
|
||||||
branch: true
|
branch: true
|
||||||
delay: [1,1]
|
delay: 1
|
||||||
- CBNEZ:
|
- CBNEZ:
|
||||||
encoding: 0b1110000000000001
|
encoding: 0b1110000000000001
|
||||||
mask: 0b1110000000000011
|
mask: 0b1110000000000011
|
||||||
attributes: [[name:no_cont], [name:cond]]
|
|
||||||
size: 16
|
size: 16
|
||||||
branch: true
|
branch: true
|
||||||
delay: [1,1]
|
delay: 1
|
||||||
- CSLLI:
|
- CSLLI:
|
||||||
encoding: 0b0000000000000010
|
encoding: 0b0000000000000010
|
||||||
mask: 0b1111000000000011
|
mask: 0b1111000000000011
|
||||||
@ -497,7 +483,6 @@ RV32IC:
|
|||||||
- CJR:
|
- CJR:
|
||||||
encoding: 0b1000000000000010
|
encoding: 0b1000000000000010
|
||||||
mask: 0b1111000001111111
|
mask: 0b1111000001111111
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 16
|
size: 16
|
||||||
branch: true
|
branch: true
|
||||||
delay: 1
|
delay: 1
|
||||||
@ -510,14 +495,12 @@ RV32IC:
|
|||||||
- CJALR:
|
- CJALR:
|
||||||
encoding: 0b1001000000000010
|
encoding: 0b1001000000000010
|
||||||
mask: 0b1111000001111111
|
mask: 0b1111000001111111
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 16
|
size: 16
|
||||||
branch: true
|
branch: true
|
||||||
delay: 1
|
delay: 1
|
||||||
- CEBREAK:
|
- CEBREAK:
|
||||||
encoding: 0b1001000000000010
|
encoding: 0b1001000000000010
|
||||||
mask: 0b1111111111111111
|
mask: 0b1111111111111111
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 16
|
size: 16
|
||||||
branch: false
|
branch: false
|
||||||
delay: 1
|
delay: 1
|
||||||
@ -530,7 +513,6 @@ RV32IC:
|
|||||||
- DII:
|
- DII:
|
||||||
encoding: 0b0000000000000000
|
encoding: 0b0000000000000000
|
||||||
mask: 0b1111111111111111
|
mask: 0b1111111111111111
|
||||||
attributes: [[name:no_cont]]
|
|
||||||
size: 16
|
size: 16
|
||||||
branch: false
|
branch: false
|
||||||
delay: 1
|
delay: 1
|
||||||
|
35
cmake/flink.cmake
Normal file
35
cmake/flink.cmake
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# according to https://github.com/horance-liu/flink.cmake/tree/master
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
include(CMakeParseArguments)
|
||||||
|
|
||||||
|
function(target_do_force_link_libraries target visibility lib)
|
||||||
|
if(MSVC)
|
||||||
|
target_link_libraries(${target} ${visibility} "/WHOLEARCHIVE:${lib}")
|
||||||
|
elseif(APPLE)
|
||||||
|
target_link_libraries(${target} ${visibility} -Wl,-force_load ${lib})
|
||||||
|
else()
|
||||||
|
target_link_libraries(${target} ${visibility} -Wl,--whole-archive ${lib} -Wl,--no-whole-archive)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(target_force_link_libraries target)
|
||||||
|
cmake_parse_arguments(FLINK
|
||||||
|
""
|
||||||
|
""
|
||||||
|
"PUBLIC;INTERFACE;PRIVATE"
|
||||||
|
${ARGN}
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(lib IN LISTS FLINK_PUBLIC)
|
||||||
|
target_do_force_link_libraries(${target} PUBLIC ${lib})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(lib IN LISTS FLINK_INTERFACE)
|
||||||
|
target_do_force_link_libraries(${target} INTERFACE ${lib})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(lib IN LISTS FLINK_PRIVATE)
|
||||||
|
target_do_force_link_libraries(${target} PRIVATE ${lib})
|
||||||
|
endforeach()
|
||||||
|
endfunction()
|
@ -1,8 +1,8 @@
|
|||||||
import "ISA/RV32I.core_desc"
|
import "ISA/RVI.core_desc"
|
||||||
import "ISA/RVM.core_desc"
|
import "ISA/RVM.core_desc"
|
||||||
import "ISA/RVC.core_desc"
|
import "ISA/RVC.core_desc"
|
||||||
|
|
||||||
Core TGC_C provides RV32I, Zicsr, Zifencei, RV32M, RV32IC {
|
Core TGC5C provides RV32I, Zicsr, Zifencei, RV32M, RV32IC {
|
||||||
architectural_state {
|
architectural_state {
|
||||||
XLEN=32;
|
XLEN=32;
|
||||||
// definitions for the architecture wrapper
|
// definitions for the architecture wrapper
|
||||||
|
@ -70,7 +70,7 @@ uint8_t *${coreDef.name.toLowerCase()}::get_regs_base_ptr() {
|
|||||||
return reinterpret_cast<uint8_t*>(®);
|
return reinterpret_cast<uint8_t*>(®);
|
||||||
}
|
}
|
||||||
|
|
||||||
${coreDef.name.toLowerCase()}::phys_addr_t ${coreDef.name.toLowerCase()}::virt2phys(const iss::addr_t &pc) {
|
${coreDef.name.toLowerCase()}::phys_addr_t ${coreDef.name.toLowerCase()}::virt2phys(const iss::addr_t &addr) {
|
||||||
return phys_addr_t(pc); // change logical address to physical address
|
return phys_addr_t(addr.access, addr.space, addr.val&traits<${coreDef.name.toLowerCase()}>::addr_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ def byteSize(int size){
|
|||||||
return 128;
|
return 128;
|
||||||
}
|
}
|
||||||
def getCString(def val){
|
def getCString(def val){
|
||||||
return val.toString()
|
return val.toString()+'ULL'
|
||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
#ifndef _${coreDef.name.toUpperCase()}_H_
|
#ifndef _${coreDef.name.toUpperCase()}_H_
|
||||||
@ -76,10 +76,10 @@ template <> struct traits<${coreDef.name.toLowerCase()}> {
|
|||||||
constexpr static char const* const core_type = "${coreDef.name}";
|
constexpr static char const* const core_type = "${coreDef.name}";
|
||||||
|
|
||||||
static constexpr std::array<const char*, ${registers.size}> reg_names{
|
static constexpr std::array<const char*, ${registers.size}> reg_names{
|
||||||
{"${registers.collect{it.name}.join('", "')}"}};
|
{"${registers.collect{it.name.toLowerCase()}.join('", "')}"}};
|
||||||
|
|
||||||
static constexpr std::array<const char*, ${registers.size}> reg_aliases{
|
static constexpr std::array<const char*, ${registers.size}> reg_aliases{
|
||||||
{"${registers.collect{it.alias}.join('", "')}"}};
|
{"${registers.collect{it.alias.toLowerCase()}.join('", "')}"}};
|
||||||
|
|
||||||
enum constants {${constants.collect{c -> c.name+"="+getCString(c.value)}.join(', ')}};
|
enum constants {${constants.collect{c -> c.name+"="+getCString(c.value)}.join(', ')}};
|
||||||
|
|
||||||
@ -137,14 +137,6 @@ struct ${coreDef.name.toLowerCase()}: public arch_if {
|
|||||||
|
|
||||||
inline uint64_t stop_code() { return interrupt_sim; }
|
inline uint64_t stop_code() { return interrupt_sim; }
|
||||||
|
|
||||||
inline phys_addr_t v2p(const iss::addr_t& addr){
|
|
||||||
if (addr.space != traits<${coreDef.name.toLowerCase()}>::MEM || addr.type == iss::address_type::PHYSICAL ||
|
|
||||||
addr_mode[static_cast<uint16_t>(addr.access)&0x3]==address_type::PHYSICAL) {
|
|
||||||
return phys_addr_t(addr.access, addr.space, addr.val&traits<${coreDef.name.toLowerCase()}>::addr_mask);
|
|
||||||
} else
|
|
||||||
return virt2phys(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual phys_addr_t virt2phys(const iss::addr_t& addr);
|
virtual phys_addr_t virt2phys(const iss::addr_t& addr);
|
||||||
|
|
||||||
virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; }
|
virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; }
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
#include "${coreDef.name.toLowerCase()}.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <array>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace iss {
|
|
||||||
namespace arch {
|
|
||||||
namespace {
|
|
||||||
// according to
|
|
||||||
// https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation
|
|
||||||
#ifdef __GCC__
|
|
||||||
constexpr size_t bit_count(uint32_t u) { return __builtin_popcount(u); }
|
|
||||||
#elif __cplusplus < 201402L
|
|
||||||
constexpr size_t uCount(uint32_t u) { return u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); }
|
|
||||||
constexpr size_t bit_count(uint32_t u) { return ((uCount(u) + (uCount(u) >> 3)) & 030707070707) % 63; }
|
|
||||||
#else
|
|
||||||
constexpr size_t bit_count(uint32_t u) {
|
|
||||||
size_t uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
|
|
||||||
return ((uCount + (uCount >> 3)) & 030707070707) % 63;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using opcode_e = traits<${coreDef.name.toLowerCase()}>::opcode_e;
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* start opcode definitions
|
|
||||||
****************************************************************************/
|
|
||||||
struct instruction_desriptor {
|
|
||||||
size_t length;
|
|
||||||
uint32_t value;
|
|
||||||
uint32_t mask;
|
|
||||||
opcode_e op;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::array<instruction_desriptor, ${instructions.size}> instr_descr = {{
|
|
||||||
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
|
|
||||||
{${instr.length}, ${instr.encoding}, ${instr.mask}, opcode_e::${instr.instruction.name}},<%}%>
|
|
||||||
}};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct instruction_decoder<${coreDef.name.toLowerCase()}> {
|
|
||||||
using opcode_e = traits<${coreDef.name.toLowerCase()}>::opcode_e;
|
|
||||||
using code_word_t=traits<${coreDef.name.toLowerCase()}>::code_word_t;
|
|
||||||
|
|
||||||
struct instruction_pattern {
|
|
||||||
uint32_t value;
|
|
||||||
uint32_t mask;
|
|
||||||
opcode_e id;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<std::vector<instruction_pattern>, 4> qlut;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
unsigned decode_instruction(T);
|
|
||||||
|
|
||||||
instruction_decoder() {
|
|
||||||
for (auto instr : instr_descr) {
|
|
||||||
auto quadrant = instr.value & 0x3;
|
|
||||||
qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
|
|
||||||
}
|
|
||||||
for(auto& lut: qlut){
|
|
||||||
std::sort(std::begin(lut), std::end(lut), [](instruction_pattern const& a, instruction_pattern const& b){
|
|
||||||
return bit_count(a.mask) < bit_count(b.mask);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
unsigned instruction_decoder<${coreDef.name.toLowerCase()}>::decode_instruction<traits<${coreDef.name.toLowerCase()}>::code_word_t>(traits<${coreDef.name.toLowerCase()}>::code_word_t instr){
|
|
||||||
auto res = std::find_if(std::begin(qlut[instr&0x3]), std::end(qlut[instr&0x3]), [instr](instruction_pattern const& e){
|
|
||||||
return !((instr&e.mask) ^ e.value );
|
|
||||||
});
|
|
||||||
return static_cast<unsigned>(res!=std::end(qlut[instr&0x3])? res->id : opcode_e::MAX_OPCODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<instruction_decoder<${coreDef.name.toLowerCase()}>> traits<${coreDef.name.toLowerCase()}>::get_decoder(){
|
|
||||||
return std::make_unique<instruction_decoder<${coreDef.name.toLowerCase()}>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
74
gen_input/templates/CORENAME_sysc.cpp.gtl
Normal file
74
gen_input/templates/CORENAME_sysc.cpp.gtl
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (C) 2023 MINRES Technologies GmbH
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include "iss_factory.h"
|
||||||
|
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
|
||||||
|
#include <iss/arch/riscv_hart_m_p.h>
|
||||||
|
#include <iss/arch/riscv_hart_mu_p.h>
|
||||||
|
#include "sc_core_adapter.h"
|
||||||
|
#include "core_complex.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace iss {
|
||||||
|
namespace interp {
|
||||||
|
using namespace sysc;
|
||||||
|
volatile std::array<bool, 2> ${coreDef.name.toLowerCase()}_init = {
|
||||||
|
iss_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|interp", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
|
||||||
|
auto* cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
||||||
|
auto* cpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::${coreDef.name.toLowerCase()}>>(cc);
|
||||||
|
return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::${coreDef.name.toLowerCase()}*>(cpu), gdb_port)}};
|
||||||
|
}),
|
||||||
|
iss_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|interp", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
|
||||||
|
auto* cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
||||||
|
auto* cpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::${coreDef.name.toLowerCase()}>>(cc);
|
||||||
|
return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::${coreDef.name.toLowerCase()}*>(cpu), gdb_port)}};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#if defined(WITH_TCC)
|
||||||
|
namespace tcc {
|
||||||
|
using namespace sysc;
|
||||||
|
volatile std::array<bool, 2> ${coreDef.name.toLowerCase()}_init = {
|
||||||
|
iss_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|tcc", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
|
||||||
|
auto* cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
||||||
|
auto* cpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::${coreDef.name.toLowerCase()}>>(cc);
|
||||||
|
return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::${coreDef.name.toLowerCase()}*>(cpu), gdb_port)}};
|
||||||
|
}),
|
||||||
|
iss_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|tcc", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
|
||||||
|
auto* cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
||||||
|
auto* cpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::${coreDef.name.toLowerCase()}>>(cc);
|
||||||
|
return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::${coreDef.name.toLowerCase()}*>(cpu), gdb_port)}};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
@ -43,6 +43,7 @@ def nativeTypeSize(int size){
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <boost/coroutine2/all.hpp>
|
#include <boost/coroutine2/all.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
#define FMT_HEADER_ONLY
|
#define FMT_HEADER_ONLY
|
||||||
@ -59,6 +60,10 @@ using namespace iss::arch;
|
|||||||
using namespace iss::debugger;
|
using namespace iss::debugger;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
|
struct memory_access_exception : public std::exception{
|
||||||
|
memory_access_exception(){}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename ARCH> class vm_impl : public iss::interp::vm_base<ARCH> {
|
template <typename ARCH> class vm_impl : public iss::interp::vm_base<ARCH> {
|
||||||
public:
|
public:
|
||||||
using traits = arch::traits<ARCH>;
|
using traits = arch::traits<ARCH>;
|
||||||
@ -91,30 +96,9 @@ protected:
|
|||||||
|
|
||||||
inline const char *name(size_t index){return index<traits::reg_aliases.size()?traits::reg_aliases[index]:"illegal";}
|
inline const char *name(size_t index){return index<traits::reg_aliases.size()?traits::reg_aliases[index]:"illegal";}
|
||||||
|
|
||||||
typename arch::traits<ARCH>::opcode_e decode_inst_id(code_word_t instr);
|
|
||||||
virt_addr_t execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit) override;
|
virt_addr_t execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit) override;
|
||||||
|
|
||||||
// some compile time constants
|
// some compile time constants
|
||||||
// enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 };
|
|
||||||
enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 };
|
|
||||||
enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 };
|
|
||||||
enum {
|
|
||||||
LUT_SIZE = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK32)),
|
|
||||||
LUT_SIZE_C = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK16))
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<compile_func, LUT_SIZE> lut;
|
|
||||||
|
|
||||||
std::array<compile_func, LUT_SIZE_C> lut_00, lut_01, lut_10;
|
|
||||||
std::array<compile_func, LUT_SIZE> lut_11;
|
|
||||||
|
|
||||||
struct instruction_pattern {
|
|
||||||
uint32_t value;
|
|
||||||
uint32_t mask;
|
|
||||||
typename arch::traits<ARCH>::opcode_e id;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<std::vector<instruction_pattern>, 4> qlut;
|
|
||||||
|
|
||||||
inline void raise(uint16_t trap_id, uint16_t cause){
|
inline void raise(uint16_t trap_id, uint16_t cause){
|
||||||
auto trap_val = 0x80ULL << 24 | (cause << 16) | trap_id;
|
auto trap_val = 0x80ULL << 24 | (cause << 16) | trap_id;
|
||||||
@ -158,30 +142,96 @@ private:
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* start opcode definitions
|
* start opcode definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
struct InstructionDesriptor {
|
struct instruction_descriptor {
|
||||||
size_t length;
|
size_t length;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
typename arch::traits<ARCH>::opcode_e op;
|
typename arch::traits<ARCH>::opcode_e op;
|
||||||
};
|
};
|
||||||
|
struct decoding_tree_node{
|
||||||
|
std::vector<instruction_descriptor> instrs;
|
||||||
|
std::vector<decoding_tree_node*> children;
|
||||||
|
uint32_t submask = std::numeric_limits<uint32_t>::max();
|
||||||
|
uint32_t value;
|
||||||
|
decoding_tree_node(uint32_t value) : value(value){}
|
||||||
|
};
|
||||||
|
|
||||||
const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{
|
decoding_tree_node* root {nullptr};
|
||||||
|
const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{
|
||||||
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
|
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
|
||||||
{${instr.length}, ${instr.encoding}, ${instr.mask}, arch::traits<ARCH>::opcode_e::${instr.instruction.name}},<%}%>
|
{${instr.length}, ${instr.encoding}, ${instr.mask}, arch::traits<ARCH>::opcode_e::${instr.instruction.name}},<%}%>
|
||||||
}};
|
}};
|
||||||
|
|
||||||
//static constexpr typename traits::addr_t upper_bits = ~traits::PGMASK;
|
|
||||||
iss::status fetch_ins(virt_addr_t pc, uint8_t * data){
|
iss::status fetch_ins(virt_addr_t pc, uint8_t * data){
|
||||||
auto phys_pc = this->core.v2p(pc);
|
if(this->core.has_mmu()) {
|
||||||
//if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
|
auto phys_pc = this->core.virt2phys(pc);
|
||||||
// if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err;
|
// if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
|
||||||
// if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction
|
// if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err;
|
||||||
// if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok) return iss::Err;
|
// if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction
|
||||||
//} else {
|
// if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok)
|
||||||
if (this->core.read(phys_pc, 4, data) != iss::Ok) return iss::Err;
|
// return iss::Err;
|
||||||
//}
|
// } else {
|
||||||
|
if (this->core.read(phys_pc, 4, data) != iss::Ok)
|
||||||
|
return iss::Err;
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
if (this->core.read(phys_addr_t(pc.access, pc.space, pc.val), 4, data) != iss::Ok)
|
||||||
|
return iss::Err;
|
||||||
|
|
||||||
|
}
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void populate_decoding_tree(decoding_tree_node* root){
|
||||||
|
//create submask
|
||||||
|
for(auto instr: root->instrs){
|
||||||
|
root->submask &= instr.mask;
|
||||||
|
}
|
||||||
|
//put each instr according to submask&encoding into children
|
||||||
|
for(auto instr: root->instrs){
|
||||||
|
bool foundMatch = false;
|
||||||
|
for(auto child: root->children){
|
||||||
|
//use value as identifying trait
|
||||||
|
if(child->value == (instr.value&root->submask)){
|
||||||
|
child->instrs.push_back(instr);
|
||||||
|
foundMatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!foundMatch){
|
||||||
|
decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask);
|
||||||
|
child->instrs.push_back(instr);
|
||||||
|
root->children.push_back(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root->instrs.clear();
|
||||||
|
//call populate_decoding_tree for all children
|
||||||
|
if(root->children.size() >1)
|
||||||
|
for(auto child: root->children){
|
||||||
|
populate_decoding_tree(child);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//sort instrs by value of the mask, this works bc we want to have the least restrictive one last
|
||||||
|
std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) {
|
||||||
|
return instr1.mask > instr2.mask;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typename arch::traits<ARCH>::opcode_e decode_instr(decoding_tree_node* node, code_word_t word){
|
||||||
|
if(!node->children.size()){
|
||||||
|
if(node->instrs.size() == 1) return node->instrs[0].op;
|
||||||
|
for(auto instr : node->instrs){
|
||||||
|
if((instr.mask&word) == instr.value) return instr.op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for(auto child : node->children){
|
||||||
|
if (child->value == (node->submask&word)){
|
||||||
|
return decode_instr(child, word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arch::traits<ARCH>::opcode_e::MAX_OPCODE;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
|
template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
|
||||||
@ -208,16 +258,11 @@ constexpr size_t bit_count(uint32_t u) {
|
|||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
|
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
|
||||||
: vm_base<ARCH>(core, core_id, cluster_id) {
|
: vm_base<ARCH>(core, core_id, cluster_id) {
|
||||||
unsigned id=0;
|
root = new decoding_tree_node(std::numeric_limits<uint32_t>::max());
|
||||||
for (auto instr : instr_descr) {
|
for(auto instr:instr_descr){
|
||||||
auto quadrant = instr.value & 0x3;
|
root->instrs.push_back(instr);
|
||||||
qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
|
|
||||||
}
|
|
||||||
for(auto& lut: qlut){
|
|
||||||
std::sort(std::begin(lut), std::end(lut), [](instruction_pattern const& a, instruction_pattern const& b){
|
|
||||||
return bit_count(a.mask) > bit_count(b.mask);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
populate_decoding_tree(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_count_limit_enabled(finish_cond_e cond){
|
inline bool is_count_limit_enabled(finish_cond_e cond){
|
||||||
@ -228,14 +273,6 @@ inline bool is_jump_to_self_enabled(finish_cond_e cond){
|
|||||||
return (cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF;
|
return (cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ARCH>
|
|
||||||
typename arch::traits<ARCH>::opcode_e vm_impl<ARCH>::decode_inst_id(code_word_t instr){
|
|
||||||
for(auto& e: qlut[instr&0x3]){
|
|
||||||
if(!((instr&e.mask) ^ e.value )) return e.id;
|
|
||||||
}
|
|
||||||
return arch::traits<ARCH>::opcode_e::MAX_OPCODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){
|
typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){
|
||||||
auto pc=start;
|
auto pc=start;
|
||||||
@ -257,32 +294,34 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
} else {
|
} else {
|
||||||
if (is_jump_to_self_enabled(cond) &&
|
if (is_jump_to_self_enabled(cond) &&
|
||||||
(instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
|
(instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
|
||||||
auto inst_id = decode_inst_id(instr);
|
auto inst_id = decode_instr(root, instr);
|
||||||
// pre execution stuff
|
// pre execution stuff
|
||||||
this->core.reg.last_branch = 0;
|
this->core.reg.last_branch = 0;
|
||||||
if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id));
|
if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id));
|
||||||
switch(inst_id){<%instructions.eachWithIndex{instr, idx -> %>
|
try{
|
||||||
case arch::traits<ARCH>::opcode_e::${instr.name}: {
|
switch(inst_id){<%instructions.eachWithIndex{instr, idx -> %>
|
||||||
<%instr.fields.eachLine{%>${it}
|
case arch::traits<ARCH>::opcode_e::${instr.name}: {
|
||||||
<%}%>if(this->disass_enabled){
|
<%instr.fields.eachLine{%>${it}
|
||||||
/* generate console output when executing the command */<%instr.disass.eachLine{%>
|
<%}%>if(this->disass_enabled){
|
||||||
|
/* generate console output when executing the command */<%instr.disass.eachLine{%>
|
||||||
|
${it}<%}%>
|
||||||
|
}
|
||||||
|
// used registers<%instr.usedVariables.each{ k,v->
|
||||||
|
if(v.isArray) {%>
|
||||||
|
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}0]);<% }else{ %>
|
||||||
|
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}]);
|
||||||
|
<%}}%>// calculate next pc value
|
||||||
|
*NEXT_PC = *PC + ${instr.length/8};
|
||||||
|
// execute instruction<%instr.behavior.eachLine{%>
|
||||||
${it}<%}%>
|
${it}<%}%>
|
||||||
|
break;
|
||||||
|
}// @suppress("No break at end of case")<%}%>
|
||||||
|
default: {
|
||||||
|
*NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2);
|
||||||
|
raise(0, 2);
|
||||||
}
|
}
|
||||||
// used registers<%instr.usedVariables.each{ k,v->
|
}
|
||||||
if(v.isArray) {%>
|
}catch(memory_access_exception& e){}
|
||||||
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}0]);<% }else{ %>
|
|
||||||
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}]);
|
|
||||||
<%}}%>// calculate next pc value
|
|
||||||
*NEXT_PC = *PC + ${instr.length/8};
|
|
||||||
// execute instruction<%instr.behavior.eachLine{%>
|
|
||||||
${it}<%}%>
|
|
||||||
TRAP_${instr.name}:break;
|
|
||||||
}// @suppress("No break at end of case")<%}%>
|
|
||||||
default: {
|
|
||||||
*NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2);
|
|
||||||
raise(0, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// post execution stuff
|
// post execution stuff
|
||||||
process_spawn_blocks();
|
process_spawn_blocks();
|
||||||
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, static_cast<unsigned>(inst_id));
|
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, static_cast<unsigned>(inst_id));
|
||||||
@ -304,7 +343,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace ${coreDef.name.toLowerCase()}
|
||||||
|
|
||||||
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) {
|
||||||
@ -320,7 +359,7 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD
|
|||||||
#include <iss/arch/riscv_hart_mu_p.h>
|
#include <iss/arch/riscv_hart_mu_p.h>
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace {
|
namespace {
|
||||||
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*) -> 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);
|
||||||
@ -336,8 +375,3 @@ std::array<bool, 2> dummy = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extern "C" {
|
|
||||||
bool* get_${coreDef.name.toLowerCase()}_interp_creators() {
|
|
||||||
return iss::dummy.data();
|
|
||||||
}
|
|
||||||
}
|
|
@ -30,10 +30,9 @@
|
|||||||
*
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
|
||||||
#include <iss/debugger/gdb_session.h>
|
#include <iss/debugger/gdb_session.h>
|
||||||
#include <iss/debugger/server.h>
|
#include <iss/debugger/server.h>
|
||||||
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
|
|
||||||
#include <iss/arch/riscv_hart_m_p.h>
|
|
||||||
#include <iss/iss.h>
|
#include <iss/iss.h>
|
||||||
#include <iss/llvm/vm_base.h>
|
#include <iss/llvm/vm_base.h>
|
||||||
#include <util/logging.h>
|
#include <util/logging.h>
|
||||||
@ -59,6 +58,7 @@ using namespace iss::debugger;
|
|||||||
|
|
||||||
template <typename ARCH> class vm_impl : public iss::llvm::vm_base<ARCH> {
|
template <typename ARCH> class vm_impl : public iss::llvm::vm_base<ARCH> {
|
||||||
public:
|
public:
|
||||||
|
using traits = arch::traits<ARCH>;
|
||||||
using super = typename iss::llvm::vm_base<ARCH>;
|
using super = typename iss::llvm::vm_base<ARCH>;
|
||||||
using virt_addr_t = typename super::virt_addr_t;
|
using virt_addr_t = typename super::virt_addr_t;
|
||||||
using phys_addr_t = typename super::phys_addr_t;
|
using phys_addr_t = typename super::phys_addr_t;
|
||||||
@ -81,7 +81,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
using vm_base<ARCH>::get_reg_ptr;
|
using vm_base<ARCH>::get_reg_ptr;
|
||||||
|
|
||||||
inline const char *name(size_t index){return traits<ARCH>::reg_aliases.at(index);}
|
inline const char *name(size_t index){return traits::reg_aliases.at(index);}
|
||||||
|
|
||||||
template <typename T> inline ConstantInt *size(T type) {
|
template <typename T> inline ConstantInt *size(T type) {
|
||||||
return ConstantInt::get(getContext(), APInt(32, type->getType()->getScalarSizeInBits()));
|
return ConstantInt::get(getContext(), APInt(32, type->getType()->getScalarSizeInBits()));
|
||||||
@ -89,7 +89,7 @@ protected:
|
|||||||
|
|
||||||
void setup_module(Module* m) override {
|
void setup_module(Module* m) override {
|
||||||
super::setup_module(m);
|
super::setup_module(m);
|
||||||
iss::llvm::fp_impl::add_fp_functions_2_module(m, traits<ARCH>::FP_REGS_SIZE, traits<ARCH>::XLEN);
|
iss::llvm::fp_impl::add_fp_functions_2_module(m, traits::FP_REGS_SIZE, traits::XLEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Value *gen_choose(Value *cond, Value *trueVal, Value *falseVal, unsigned size) {
|
inline Value *gen_choose(Value *cond, Value *trueVal, Value *falseVal, unsigned size) {
|
||||||
@ -111,92 +111,74 @@ protected:
|
|||||||
void gen_trap_check(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(get_reg_ptr(i), false);
|
return this->builder.CreateLoad(this->get_typeptr(i), get_reg_ptr(i), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void gen_set_pc(virt_addr_t pc, unsigned reg_num) {
|
inline void gen_set_pc(virt_addr_t pc, unsigned reg_num) {
|
||||||
Value *next_pc_v = this->builder.CreateSExtOrTrunc(this->gen_const(traits<ARCH>::XLEN, pc.val),
|
Value *next_pc_v = this->builder.CreateSExtOrTrunc(this->gen_const(traits::XLEN, pc.val),
|
||||||
this->get_type(traits<ARCH>::XLEN));
|
this->get_type(traits::XLEN));
|
||||||
this->builder.CreateStore(next_pc_v, get_reg_ptr(reg_num), true);
|
this->builder.CreateStore(next_pc_v, get_reg_ptr(reg_num), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// some compile time constants
|
// some compile time constants
|
||||||
// enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 };
|
|
||||||
enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 };
|
|
||||||
enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 };
|
|
||||||
enum { LUT_SIZE = 1 << util::bit_count(EXTR_MASK32), LUT_SIZE_C = 1 << util::bit_count(EXTR_MASK16) };
|
|
||||||
|
|
||||||
using this_class = vm_impl<ARCH>;
|
using this_class = vm_impl<ARCH>;
|
||||||
using compile_func = std::tuple<continuation_e, BasicBlock *> (this_class::*)(virt_addr_t &pc,
|
using compile_func = std::tuple<continuation_e, BasicBlock *> (this_class::*)(virt_addr_t &pc,
|
||||||
code_word_t instr,
|
code_word_t instr,
|
||||||
BasicBlock *bb);
|
BasicBlock *bb);
|
||||||
std::array<compile_func, LUT_SIZE> lut;
|
template<unsigned W, typename U, typename S = typename std::make_signed<U>::type>
|
||||||
|
inline S sext(U from) {
|
||||||
std::array<compile_func, LUT_SIZE_C> lut_00, lut_01, lut_10;
|
auto mask = (1ULL<<W) - 1;
|
||||||
std::array<compile_func, LUT_SIZE> lut_11;
|
auto sign_mask = 1ULL<<(W-1);
|
||||||
|
return (from & mask) | ((from & sign_mask) ? ~mask : 0);
|
||||||
std::array<compile_func *, 4> qlut;
|
}
|
||||||
|
|
||||||
std::array<const uint32_t, 4> lutmasks = {{EXTR_MASK16, EXTR_MASK16, EXTR_MASK16, EXTR_MASK32}};
|
|
||||||
|
|
||||||
void expand_bit_mask(int pos, uint32_t mask, uint32_t value, uint32_t valid, uint32_t idx, compile_func lut[],
|
|
||||||
compile_func f) {
|
|
||||||
if (pos < 0) {
|
|
||||||
lut[idx] = f;
|
|
||||||
} else {
|
|
||||||
auto bitmask = 1UL << pos;
|
|
||||||
if ((mask & bitmask) == 0) {
|
|
||||||
expand_bit_mask(pos - 1, mask, value, valid, idx, lut, f);
|
|
||||||
} else {
|
|
||||||
if ((valid & bitmask) == 0) {
|
|
||||||
expand_bit_mask(pos - 1, mask, value, valid, (idx << 1), lut, f);
|
|
||||||
expand_bit_mask(pos - 1, mask, value, valid, (idx << 1) + 1, lut, f);
|
|
||||||
} else {
|
|
||||||
auto new_val = idx << 1;
|
|
||||||
if ((value & bitmask) != 0) new_val++;
|
|
||||||
expand_bit_mask(pos - 1, mask, value, valid, new_val, lut, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32_t extract_fields(uint32_t val) { return extract_fields(29, val >> 2, lutmasks[val & 0x3], 0); }
|
|
||||||
|
|
||||||
uint32_t extract_fields(int pos, uint32_t val, uint32_t mask, uint32_t lut_val) {
|
|
||||||
if (pos >= 0) {
|
|
||||||
auto bitmask = 1UL << pos;
|
|
||||||
if ((mask & bitmask) == 0) {
|
|
||||||
lut_val = extract_fields(pos - 1, val, mask, lut_val);
|
|
||||||
} else {
|
|
||||||
auto new_val = lut_val << 1;
|
|
||||||
if ((val & bitmask) != 0) new_val++;
|
|
||||||
lut_val = extract_fields(pos - 1, val, mask, new_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lut_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* start opcode definitions
|
* start opcode definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
struct InstructionDesriptor {
|
struct instruction_descriptor {
|
||||||
size_t length;
|
size_t length;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
compile_func op;
|
compile_func op;
|
||||||
};
|
};
|
||||||
|
struct decoding_tree_node{
|
||||||
|
std::vector<instruction_descriptor> instrs;
|
||||||
|
std::vector<decoding_tree_node*> children;
|
||||||
|
uint32_t submask = std::numeric_limits<uint32_t>::max();
|
||||||
|
uint32_t value;
|
||||||
|
decoding_tree_node(uint32_t value) : value(value){}
|
||||||
|
};
|
||||||
|
|
||||||
const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{
|
decoding_tree_node* root {nullptr};
|
||||||
|
|
||||||
|
const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{
|
||||||
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
|
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
|
||||||
/* instruction ${instr.instruction.name} */
|
/* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */
|
||||||
{${instr.length}, ${instr.value}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
|
{${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
|
||||||
}};
|
}};
|
||||||
|
|
||||||
/* 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){<%instr.code.eachLine{%>
|
std::tuple<continuation_e, BasicBlock*> __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr, BasicBlock* bb){
|
||||||
${it}<%}%>
|
bb->setName(fmt::format("${instr.name}_0x{:X}",pc.val));
|
||||||
|
this->gen_sync(PRE_SYNC,${idx});
|
||||||
|
uint64_t PC = pc.val;
|
||||||
|
<%instr.fields.eachLine{%>${it}
|
||||||
|
<%}%>if(this->disass_enabled){
|
||||||
|
/* generate console output when executing the command */<%instr.disass.eachLine{%>
|
||||||
|
${it}<%}%>
|
||||||
|
}
|
||||||
|
auto cur_pc_val = this->gen_const(32,pc.val);
|
||||||
|
pc=pc+ ${instr.length/8};
|
||||||
|
this->gen_set_pc(pc, traits::NEXT_PC);
|
||||||
|
<%instr.behavior.eachLine{%>${it}
|
||||||
|
<%}%>
|
||||||
|
this->gen_trap_check(bb);
|
||||||
|
this->gen_sync(POST_SYNC, ${idx});
|
||||||
|
this->builder.CreateBr(bb);
|
||||||
|
return returnValue;
|
||||||
}
|
}
|
||||||
<%}%>
|
<%}%>
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -204,23 +186,75 @@ private:
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
std::tuple<continuation_e, BasicBlock *> illegal_intruction(virt_addr_t &pc, code_word_t instr, BasicBlock *bb) {
|
std::tuple<continuation_e, BasicBlock *> illegal_intruction(virt_addr_t &pc, code_word_t instr, BasicBlock *bb) {
|
||||||
this->gen_sync(iss::PRE_SYNC, instr_descr.size());
|
this->gen_sync(iss::PRE_SYNC, instr_descr.size());
|
||||||
this->builder.CreateStore(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::NEXT_PC), true),
|
this->builder.CreateStore(this->builder.CreateLoad(this->get_typeptr(traits::NEXT_PC), get_reg_ptr(traits::NEXT_PC), true),
|
||||||
get_reg_ptr(traits<ARCH>::PC), true);
|
get_reg_ptr(traits::PC), true);
|
||||||
this->builder.CreateStore(
|
this->builder.CreateStore(
|
||||||
this->builder.CreateAdd(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::ICOUNT), true),
|
this->builder.CreateAdd(this->builder.CreateLoad(this->get_typeptr(traits::ICOUNT), get_reg_ptr(traits::ICOUNT), true),
|
||||||
this->gen_const(64U, 1)),
|
this->gen_const(64U, 1)),
|
||||||
get_reg_ptr(traits<ARCH>::ICOUNT), true);
|
get_reg_ptr(traits::ICOUNT), true);
|
||||||
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_trap_check(this->leave_blk);
|
||||||
return std::make_tuple(BRANCH, nullptr);
|
return std::make_tuple(BRANCH, nullptr);
|
||||||
|
}
|
||||||
|
//decoding functionality
|
||||||
|
|
||||||
|
void populate_decoding_tree(decoding_tree_node* root){
|
||||||
|
//create submask
|
||||||
|
for(auto instr: root->instrs){
|
||||||
|
root->submask &= instr.mask;
|
||||||
|
}
|
||||||
|
//put each instr according to submask&encoding into children
|
||||||
|
for(auto instr: root->instrs){
|
||||||
|
bool foundMatch = false;
|
||||||
|
for(auto child: root->children){
|
||||||
|
//use value as identifying trait
|
||||||
|
if(child->value == (instr.value&root->submask)){
|
||||||
|
child->instrs.push_back(instr);
|
||||||
|
foundMatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!foundMatch){
|
||||||
|
decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask);
|
||||||
|
child->instrs.push_back(instr);
|
||||||
|
root->children.push_back(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root->instrs.clear();
|
||||||
|
//call populate_decoding_tree for all children
|
||||||
|
if(root->children.size() >1)
|
||||||
|
for(auto child: root->children){
|
||||||
|
populate_decoding_tree(child);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//sort instrs by value of the mask, this works bc we want to have the least restrictive one last
|
||||||
|
std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) {
|
||||||
|
return instr1.mask > instr2.mask;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compile_func decode_instr(decoding_tree_node* node, code_word_t word){
|
||||||
|
if(!node->children.size()){
|
||||||
|
if(node->instrs.size() == 1) return node->instrs[0].op;
|
||||||
|
for(auto instr : node->instrs){
|
||||||
|
if((instr.mask&word) == instr.value) return instr.op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for(auto child : node->children){
|
||||||
|
if (child->value == (node->submask&word)){
|
||||||
|
return decode_instr(child, word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
|
template <typename CODE_WORD> void debug_fn(CODE_WORD instr) {
|
||||||
volatile CODE_WORD x = insn;
|
volatile CODE_WORD x = instr;
|
||||||
insn = 2 * x;
|
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()); }
|
||||||
@ -228,14 +262,11 @@ template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
|
|||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
|
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
|
||||||
: vm_base<ARCH>(core, core_id, cluster_id) {
|
: vm_base<ARCH>(core, core_id, cluster_id) {
|
||||||
qlut[0] = lut_00.data();
|
root = new decoding_tree_node(std::numeric_limits<uint32_t>::max());
|
||||||
qlut[1] = lut_01.data();
|
for(auto instr:instr_descr){
|
||||||
qlut[2] = lut_10.data();
|
root->instrs.push_back(instr);
|
||||||
qlut[3] = lut_11.data();
|
|
||||||
for (auto instr : instr_descr) {
|
|
||||||
auto quantrant = instr.value & 0x3;
|
|
||||||
expand_bit_mask(29, lutmasks[quantrant], instr.value >> 2, instr.mask >> 2, 0, qlut[quantrant], instr.op);
|
|
||||||
}
|
}
|
||||||
|
populate_decoding_tree(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
@ -243,49 +274,50 @@ std::tuple<continuation_e, BasicBlock *>
|
|||||||
vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, BasicBlock *this_block) {
|
vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, BasicBlock *this_block) {
|
||||||
// we fetch at max 4 byte, alignment is 2
|
// we fetch at max 4 byte, alignment is 2
|
||||||
enum {TRAP_ID=1<<16};
|
enum {TRAP_ID=1<<16};
|
||||||
code_word_t insn = 0;
|
code_word_t instr = 0;
|
||||||
const typename traits<ARCH>::addr_t upper_bits = ~traits<ARCH>::PGMASK;
|
// const typename traits::addr_t upper_bits = ~traits::PGMASK;
|
||||||
phys_addr_t paddr(pc);
|
phys_addr_t paddr(pc);
|
||||||
auto *const data = (uint8_t *)&insn;
|
auto *const data = (uint8_t *)&instr;
|
||||||
paddr = this->core.v2p(pc);
|
if(this->core.has_mmu())
|
||||||
if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
|
paddr = this->core.virt2phys(pc);
|
||||||
auto res = this->core.read(paddr, 2, data);
|
//TODO: re-add page handling
|
||||||
if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
|
// if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
|
||||||
if ((insn & 0x3) == 0x3) { // this is a 32bit instruction
|
// auto res = this->core.read(paddr, 2, data);
|
||||||
res = this->core.read(this->core.v2p(pc + 2), 2, data + 2);
|
// if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
|
||||||
}
|
// if ((instr & 0x3) == 0x3) { // this is a 32bit instruction
|
||||||
} else {
|
// res = this->core.read(this->core.v2p(pc + 2), 2, data + 2);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
auto res = this->core.read(paddr, 4, data);
|
auto res = this->core.read(paddr, 4, data);
|
||||||
if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
|
if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
|
||||||
}
|
// }
|
||||||
if (insn == 0x0000006f || (insn&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
|
if (instr == 0x0000006f || (instr&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
|
||||||
// curr pc on stack
|
// curr pc on stack
|
||||||
++inst_cnt;
|
++inst_cnt;
|
||||||
auto lut_val = extract_fields(insn);
|
auto f = decode_instr(root, instr);
|
||||||
auto f = qlut[insn & 0x3][lut_val];
|
|
||||||
if (f == nullptr) {
|
if (f == nullptr) {
|
||||||
f = &this_class::illegal_intruction;
|
f = &this_class::illegal_intruction;
|
||||||
}
|
}
|
||||||
return (this->*f)(pc, insn, 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(get_reg_ptr(arch::traits<ARCH>::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<ARCH>::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<ARCH>::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<ARCH>::CSR, (lvl << 8) + 0x41, traits<ARCH>::XLEN / 8);
|
auto *PC_val = this->gen_read_mem(traits::CSR, (lvl << 8) + 0x41, traits::XLEN / 8);
|
||||||
this->builder.CreateStore(PC_val, get_reg_ptr(traits<ARCH>::NEXT_PC), false);
|
this->builder.CreateStore(PC_val, get_reg_ptr(traits::NEXT_PC), false);
|
||||||
this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()), get_reg_ptr(traits<ARCH>::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) {
|
||||||
@ -295,22 +327,25 @@ template <typename ARCH> void vm_impl<ARCH>::gen_wait(unsigned type) {
|
|||||||
|
|
||||||
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);
|
||||||
auto *trap_state_val = this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::TRAP_STATE), true);
|
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);
|
||||||
this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()),
|
this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()),
|
||||||
get_reg_ptr(traits<ARCH>::LAST_BRANCH), false);
|
get_reg_ptr(traits::LAST_BRANCH), false);
|
||||||
std::vector<Value *> args{this->core_ptr, this->adj_to64(trap_state_val),
|
std::vector<Value *> args{this->core_ptr, this->adj_to64(trap_state_val),
|
||||||
this->adj_to64(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::PC), false))};
|
this->adj_to64(this->builder.CreateLoad(this->get_typeptr(traits::PC), get_reg_ptr(traits::PC), false))};
|
||||||
this->builder.CreateCall(this->mod->getFunction("enter_trap"), args);
|
this->builder.CreateCall(this->mod->getFunction("enter_trap"), args);
|
||||||
auto *trap_addr_val = this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::NEXT_PC), false);
|
auto *trap_addr_val = this->builder.CreateLoad(this->get_typeptr(traits::NEXT_PC), get_reg_ptr(traits::NEXT_PC), false);
|
||||||
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> inline void vm_impl<ARCH>::gen_trap_check(BasicBlock *bb) {
|
||||||
auto *v = this->builder.CreateLoad(get_reg_ptr(arch::traits<ARCH>::TRAP_STATE), true);
|
auto* target_bb = BasicBlock::Create(this->mod->getContext(), "", this->func, bb);
|
||||||
|
auto *v = this->builder.CreateLoad(this->get_typeptr(traits::TRAP_STATE), get_reg_ptr(traits::TRAP_STATE), true);
|
||||||
this->gen_cond_branch(this->builder.CreateICmp(
|
this->gen_cond_branch(this->builder.CreateICmp(
|
||||||
ICmpInst::ICMP_EQ, v,
|
ICmpInst::ICMP_EQ, v,
|
||||||
ConstantInt::get(getContext(), APInt(v->getType()->getIntegerBitWidth(), 0))),
|
ConstantInt::get(getContext(), APInt(v->getType()->getIntegerBitWidth(), 0))),
|
||||||
bb, this->trap_blk, 1);
|
target_bb, this->trap_blk, 1);
|
||||||
|
this->builder.SetInsertPoint(target_bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ${coreDef.name.toLowerCase()}
|
} // namespace ${coreDef.name.toLowerCase()}
|
||||||
@ -323,3 +358,25 @@ 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_mu_p.h>
|
||||||
|
namespace iss {
|
||||||
|
namespace {
|
||||||
|
volatile std::array<bool, 2> dummy = {
|
||||||
|
core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|llvm", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
|
||||||
|
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);
|
||||||
|
if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
|
||||||
|
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>{
|
||||||
|
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);
|
||||||
|
if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
|
||||||
|
return {cpu_ptr{cpu}, vm_ptr{vm}};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"${coreDef.name}" : [<%instructions.eachWithIndex{instr,index -> %>${index==0?"":","}
|
|
||||||
{
|
|
||||||
"name" : "${instr.name}",
|
|
||||||
"size" : ${instr.length},
|
|
||||||
"delay" : ${generator.hasAttribute(instr.instruction, com.minres.coredsl.coreDsl.InstrAttribute.COND)?[1,1]:1}
|
|
||||||
}<%}%>
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,223 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
<%
|
|
||||||
import com.minres.coredsl.coreDsl.Register
|
|
||||||
import com.minres.coredsl.coreDsl.RegisterFile
|
|
||||||
import com.minres.coredsl.coreDsl.RegisterAlias
|
|
||||||
def getTypeSize(size){
|
|
||||||
if(size > 32) 64 else if(size > 16) 32 else if(size > 8) 16 else 8
|
|
||||||
}
|
|
||||||
def getOriginalName(reg){
|
|
||||||
if( reg.original instanceof RegisterFile) {
|
|
||||||
if( reg.index != null ) {
|
|
||||||
return reg.original.name+generator.generateHostCode(reg.index)
|
|
||||||
} else {
|
|
||||||
return reg.original.name
|
|
||||||
}
|
|
||||||
} else if(reg.original instanceof Register){
|
|
||||||
return reg.original.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def getRegisterNames(){
|
|
||||||
def regNames = []
|
|
||||||
allRegs.each { reg ->
|
|
||||||
if( reg instanceof RegisterFile) {
|
|
||||||
(reg.range.right..reg.range.left).each{
|
|
||||||
regNames+=reg.name.toLowerCase()+it
|
|
||||||
}
|
|
||||||
} else if(reg instanceof Register){
|
|
||||||
regNames+=reg.name.toLowerCase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return regNames
|
|
||||||
}
|
|
||||||
def getRegisterAliasNames(){
|
|
||||||
def regMap = allRegs.findAll{it instanceof RegisterAlias }.collectEntries {[getOriginalName(it), it.name]}
|
|
||||||
return allRegs.findAll{it instanceof Register || it instanceof RegisterFile}.collect{reg ->
|
|
||||||
if( reg instanceof RegisterFile) {
|
|
||||||
return (reg.range.right..reg.range.left).collect{ (regMap[reg.name]?:regMap[reg.name+it]?:reg.name.toLowerCase()+it).toLowerCase() }
|
|
||||||
} else if(reg instanceof Register){
|
|
||||||
regMap[reg.name]?:reg.name.toLowerCase()
|
|
||||||
}
|
|
||||||
}.flatten()
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
#ifndef _${coreDef.name.toUpperCase()}_H_
|
|
||||||
#define _${coreDef.name.toUpperCase()}_H_
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <iss/arch/traits.h>
|
|
||||||
#include <iss/arch_if.h>
|
|
||||||
#include <iss/vm_if.h>
|
|
||||||
|
|
||||||
namespace iss {
|
|
||||||
namespace arch {
|
|
||||||
|
|
||||||
struct ${coreDef.name.toLowerCase()};
|
|
||||||
|
|
||||||
template <> struct traits<${coreDef.name.toLowerCase()}> {
|
|
||||||
|
|
||||||
constexpr static char const* const core_type = "${coreDef.name}";
|
|
||||||
|
|
||||||
static constexpr std::array<const char*, ${getRegisterNames().size}> reg_names{
|
|
||||||
{"${getRegisterNames().join("\", \"")}"}};
|
|
||||||
|
|
||||||
static constexpr std::array<const char*, ${getRegisterAliasNames().size}> reg_aliases{
|
|
||||||
{"${getRegisterAliasNames().join("\", \"")}"}};
|
|
||||||
|
|
||||||
enum constants {${coreDef.constants.collect{c -> c.name+"="+c.value}.join(', ')}};
|
|
||||||
|
|
||||||
constexpr static unsigned FP_REGS_SIZE = ${coreDef.constants.find {it.name=='FLEN'}?.value?:0};
|
|
||||||
|
|
||||||
enum reg_e {<%
|
|
||||||
allRegs.each { reg ->
|
|
||||||
if( reg instanceof RegisterFile) {
|
|
||||||
(reg.range.right..reg.range.left).each{%>
|
|
||||||
${reg.name}${it},<%
|
|
||||||
}
|
|
||||||
} else if(reg instanceof Register){ %>
|
|
||||||
${reg.name},<%
|
|
||||||
}
|
|
||||||
}%>
|
|
||||||
NUM_REGS,
|
|
||||||
NEXT_${pc.name}=NUM_REGS,
|
|
||||||
TRAP_STATE,
|
|
||||||
PENDING_TRAP,
|
|
||||||
MACHINE_STATE,
|
|
||||||
LAST_BRANCH,
|
|
||||||
ICOUNT<%
|
|
||||||
allRegs.each { reg ->
|
|
||||||
if(reg instanceof RegisterAlias){ def aliasname=getOriginalName(reg)%>,
|
|
||||||
${reg.name} = ${aliasname}<%
|
|
||||||
}
|
|
||||||
}%>
|
|
||||||
};
|
|
||||||
|
|
||||||
using reg_t = uint${regDataWidth}_t;
|
|
||||||
|
|
||||||
using addr_t = uint${addrDataWidth}_t;
|
|
||||||
|
|
||||||
using code_word_t = uint${addrDataWidth}_t; //TODO: check removal
|
|
||||||
|
|
||||||
using virt_addr_t = iss::typed_addr_t<iss::address_type::VIRTUAL>;
|
|
||||||
|
|
||||||
using phys_addr_t = iss::typed_addr_t<iss::address_type::PHYSICAL>;
|
|
||||||
|
|
||||||
static constexpr std::array<const uint32_t, ${regSizes.size}> reg_bit_widths{
|
|
||||||
{${regSizes.join(",")}}};
|
|
||||||
|
|
||||||
static constexpr std::array<const uint32_t, ${regOffsets.size}> reg_byte_offsets{
|
|
||||||
{${regOffsets.join(",")}}};
|
|
||||||
|
|
||||||
static const uint64_t addr_mask = (reg_t(1) << (XLEN - 1)) | ((reg_t(1) << (XLEN - 1)) - 1);
|
|
||||||
|
|
||||||
enum sreg_flag_e { FLAGS };
|
|
||||||
|
|
||||||
enum mem_type_e { ${allSpaces.collect{s -> s.name}.join(', ')} };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ${coreDef.name.toLowerCase()}: public arch_if {
|
|
||||||
|
|
||||||
using virt_addr_t = typename traits<${coreDef.name.toLowerCase()}>::virt_addr_t;
|
|
||||||
using phys_addr_t = typename traits<${coreDef.name.toLowerCase()}>::phys_addr_t;
|
|
||||||
using reg_t = typename traits<${coreDef.name.toLowerCase()}>::reg_t;
|
|
||||||
using addr_t = typename traits<${coreDef.name.toLowerCase()}>::addr_t;
|
|
||||||
|
|
||||||
${coreDef.name.toLowerCase()}();
|
|
||||||
~${coreDef.name.toLowerCase()}();
|
|
||||||
|
|
||||||
void reset(uint64_t address=0) override;
|
|
||||||
|
|
||||||
uint8_t* get_regs_base_ptr() override;
|
|
||||||
/// deprecated
|
|
||||||
void get_reg(short idx, std::vector<uint8_t>& value) override {}
|
|
||||||
void set_reg(short idx, const std::vector<uint8_t>& value) override {}
|
|
||||||
/// deprecated
|
|
||||||
bool get_flag(int flag) override {return false;}
|
|
||||||
void set_flag(int, bool value) override {};
|
|
||||||
/// deprecated
|
|
||||||
void update_flags(operations op, uint64_t opr1, uint64_t opr2) override {};
|
|
||||||
|
|
||||||
inline uint64_t get_icount() { return reg.icount; }
|
|
||||||
|
|
||||||
inline bool should_stop() { return interrupt_sim; }
|
|
||||||
|
|
||||||
inline uint64_t stop_code() { return interrupt_sim; }
|
|
||||||
|
|
||||||
inline phys_addr_t v2p(const iss::addr_t& addr){
|
|
||||||
if (addr.space != traits<${coreDef.name.toLowerCase()}>::MEM || addr.type == iss::address_type::PHYSICAL ||
|
|
||||||
addr_mode[static_cast<uint16_t>(addr.access)&0x3]==address_type::PHYSICAL) {
|
|
||||||
return phys_addr_t(addr.access, addr.space, addr.val&traits<${coreDef.name.toLowerCase()}>::addr_mask);
|
|
||||||
} else
|
|
||||||
return virt2phys(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual phys_addr_t virt2phys(const iss::addr_t& addr);
|
|
||||||
|
|
||||||
virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; }
|
|
||||||
|
|
||||||
inline uint32_t get_last_branch() { return reg.last_branch; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
struct ${coreDef.name}_regs {<%
|
|
||||||
allRegs.each { reg ->
|
|
||||||
if( reg instanceof RegisterFile) {
|
|
||||||
(reg.range.right..reg.range.left).each{%>
|
|
||||||
uint${generator.getSize(reg)}_t ${reg.name}${it} = 0;<%
|
|
||||||
}
|
|
||||||
} else if(reg instanceof Register){ %>
|
|
||||||
uint${generator.getSize(reg)}_t ${reg.name} = 0;<%
|
|
||||||
}
|
|
||||||
}%>
|
|
||||||
uint${generator.getSize(pc)}_t NEXT_${pc.name} = 0;
|
|
||||||
uint32_t trap_state = 0, pending_trap = 0, machine_state = 0, last_branch = 0;
|
|
||||||
uint64_t icount = 0;
|
|
||||||
} reg;
|
|
||||||
|
|
||||||
std::array<address_type, 4> addr_mode;
|
|
||||||
|
|
||||||
uint64_t interrupt_sim=0;
|
|
||||||
<%
|
|
||||||
def fcsr = allRegs.find {it.name=='FCSR'}
|
|
||||||
if(fcsr != null) {%>
|
|
||||||
uint${generator.getSize(fcsr)}_t get_fcsr(){return reg.FCSR;}
|
|
||||||
void set_fcsr(uint${generator.getSize(fcsr)}_t val){reg.FCSR = val;}
|
|
||||||
<%} else { %>
|
|
||||||
uint32_t get_fcsr(){return 0;}
|
|
||||||
void set_fcsr(uint32_t val){}
|
|
||||||
<%}%>
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* _${coreDef.name.toUpperCase()}_H_ */
|
|
@ -1,107 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*******************************************************************************/
|
|
||||||
<%
|
|
||||||
import com.minres.coredsl.coreDsl.Register
|
|
||||||
import com.minres.coredsl.coreDsl.RegisterFile
|
|
||||||
import com.minres.coredsl.coreDsl.RegisterAlias
|
|
||||||
def getOriginalName(reg){
|
|
||||||
if( reg.original instanceof RegisterFile) {
|
|
||||||
if( reg.index != null ) {
|
|
||||||
return reg.original.name+generator.generateHostCode(reg.index)
|
|
||||||
} else {
|
|
||||||
return reg.original.name
|
|
||||||
}
|
|
||||||
} else if(reg.original instanceof Register){
|
|
||||||
return reg.original.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def getRegisterNames(){
|
|
||||||
def regNames = []
|
|
||||||
allRegs.each { reg ->
|
|
||||||
if( reg instanceof RegisterFile) {
|
|
||||||
(reg.range.right..reg.range.left).each{
|
|
||||||
regNames+=reg.name.toLowerCase()+it
|
|
||||||
}
|
|
||||||
} else if(reg instanceof Register){
|
|
||||||
regNames+=reg.name.toLowerCase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return regNames
|
|
||||||
}
|
|
||||||
def getRegisterAliasNames(){
|
|
||||||
def regMap = allRegs.findAll{it instanceof RegisterAlias }.collectEntries {[getOriginalName(it), it.name]}
|
|
||||||
return allRegs.findAll{it instanceof Register || it instanceof RegisterFile}.collect{reg ->
|
|
||||||
if( reg instanceof RegisterFile) {
|
|
||||||
return (reg.range.right..reg.range.left).collect{ (regMap[reg.name]?:regMap[reg.name+it]?:reg.name.toLowerCase()+it).toLowerCase() }
|
|
||||||
} else if(reg instanceof Register){
|
|
||||||
regMap[reg.name]?:reg.name.toLowerCase()
|
|
||||||
}
|
|
||||||
}.flatten()
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
#include "util/ities.h"
|
|
||||||
#include <util/logging.h>
|
|
||||||
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
using namespace iss::arch;
|
|
||||||
|
|
||||||
constexpr std::array<const char*, ${getRegisterNames().size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_names;
|
|
||||||
constexpr std::array<const char*, ${getRegisterAliasNames().size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_aliases;
|
|
||||||
constexpr std::array<const uint32_t, ${regSizes.size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_bit_widths;
|
|
||||||
constexpr std::array<const uint32_t, ${regOffsets.size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_byte_offsets;
|
|
||||||
|
|
||||||
${coreDef.name.toLowerCase()}::${coreDef.name.toLowerCase()}() {
|
|
||||||
reg.icount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
${coreDef.name.toLowerCase()}::~${coreDef.name.toLowerCase()}() = default;
|
|
||||||
|
|
||||||
void ${coreDef.name.toLowerCase()}::reset(uint64_t address) {
|
|
||||||
for(size_t i=0; i<traits<${coreDef.name.toLowerCase()}>::NUM_REGS; ++i) set_reg(i, std::vector<uint8_t>(sizeof(traits<${coreDef.name.toLowerCase()}>::reg_t),0));
|
|
||||||
reg.PC=address;
|
|
||||||
reg.NEXT_PC=reg.PC;
|
|
||||||
reg.trap_state=0;
|
|
||||||
reg.machine_state=0x3;
|
|
||||||
reg.icount=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *${coreDef.name.toLowerCase()}::get_regs_base_ptr() {
|
|
||||||
return reinterpret_cast<uint8_t*>(®);
|
|
||||||
}
|
|
||||||
|
|
||||||
${coreDef.name.toLowerCase()}::phys_addr_t ${coreDef.name.toLowerCase()}::virt2phys(const iss::addr_t &pc) {
|
|
||||||
return phys_addr_t(pc); // change logical address to physical address
|
|
||||||
}
|
|
||||||
|
|
@ -120,57 +120,7 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// some compile time constants
|
|
||||||
// enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 };
|
|
||||||
enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 };
|
|
||||||
enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 };
|
|
||||||
enum { LUT_SIZE = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK32)), LUT_SIZE_C = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK16)) };
|
|
||||||
|
|
||||||
std::array<compile_func, LUT_SIZE> lut;
|
|
||||||
|
|
||||||
std::array<compile_func, LUT_SIZE_C> lut_00, lut_01, lut_10;
|
|
||||||
std::array<compile_func, LUT_SIZE> lut_11;
|
|
||||||
|
|
||||||
std::array<compile_func *, 4> qlut;
|
|
||||||
|
|
||||||
std::array<const uint32_t, 4> lutmasks = {{EXTR_MASK16, EXTR_MASK16, EXTR_MASK16, EXTR_MASK32}};
|
|
||||||
|
|
||||||
void expand_bit_mask(int pos, uint32_t mask, uint32_t value, uint32_t valid, uint32_t idx, compile_func lut[],
|
|
||||||
compile_func f) {
|
|
||||||
if (pos < 0) {
|
|
||||||
lut[idx] = f;
|
|
||||||
} else {
|
|
||||||
auto bitmask = 1UL << pos;
|
|
||||||
if ((mask & bitmask) == 0) {
|
|
||||||
expand_bit_mask(pos - 1, mask, value, valid, idx, lut, f);
|
|
||||||
} else {
|
|
||||||
if ((valid & bitmask) == 0) {
|
|
||||||
expand_bit_mask(pos - 1, mask, value, valid, (idx << 1), lut, f);
|
|
||||||
expand_bit_mask(pos - 1, mask, value, valid, (idx << 1) + 1, lut, f);
|
|
||||||
} else {
|
|
||||||
auto new_val = idx << 1;
|
|
||||||
if ((value & bitmask) != 0) new_val++;
|
|
||||||
expand_bit_mask(pos - 1, mask, value, valid, new_val, lut, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32_t extract_fields(uint32_t val) { return extract_fields(29, val >> 2, lutmasks[val & 0x3], 0); }
|
|
||||||
|
|
||||||
uint32_t extract_fields(int pos, uint32_t val, uint32_t mask, uint32_t lut_val) {
|
|
||||||
if (pos >= 0) {
|
|
||||||
auto bitmask = 1UL << pos;
|
|
||||||
if ((mask & bitmask) == 0) {
|
|
||||||
lut_val = extract_fields(pos - 1, val, mask, lut_val);
|
|
||||||
} else {
|
|
||||||
auto new_val = lut_val << 1;
|
|
||||||
if ((val & bitmask) != 0) new_val++;
|
|
||||||
lut_val = extract_fields(pos - 1, val, mask, new_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lut_val;
|
|
||||||
}
|
|
||||||
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;
|
||||||
@ -182,14 +132,23 @@ private:
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* start opcode definitions
|
* start opcode definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
struct InstructionDesriptor {
|
struct instruction_descriptor {
|
||||||
size_t length;
|
size_t length;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
compile_func op;
|
compile_func op;
|
||||||
};
|
};
|
||||||
|
struct decoding_tree_node{
|
||||||
|
std::vector<instruction_descriptor> instrs;
|
||||||
|
std::vector<decoding_tree_node*> children;
|
||||||
|
uint32_t submask = std::numeric_limits<uint32_t>::max();
|
||||||
|
uint32_t value;
|
||||||
|
decoding_tree_node(uint32_t value) : value(value){}
|
||||||
|
};
|
||||||
|
|
||||||
const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{
|
decoding_tree_node* root {nullptr};
|
||||||
|
|
||||||
|
const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{
|
||||||
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
|
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
|
||||||
/* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */
|
/* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */
|
||||||
{${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
|
{${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
|
||||||
@ -200,6 +159,7 @@ private:
|
|||||||
compile_ret_t __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr, tu_builder& tu){
|
compile_ret_t __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr, tu_builder& tu){
|
||||||
tu("${instr.name}_{:#010x}:", pc.val);
|
tu("${instr.name}_{:#010x}:", pc.val);
|
||||||
vm_base<ARCH>::gen_sync(tu, PRE_SYNC,${idx});
|
vm_base<ARCH>::gen_sync(tu, PRE_SYNC,${idx});
|
||||||
|
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{%>
|
||||||
@ -208,11 +168,12 @@ private:
|
|||||||
auto cur_pc_val = tu.constant(pc.val, traits::reg_bit_widths[traits::PC]);
|
auto cur_pc_val = tu.constant(pc.val, traits::reg_bit_widths[traits::PC]);
|
||||||
pc=pc+ ${instr.length/8};
|
pc=pc+ ${instr.length/8};
|
||||||
gen_set_pc(tu, pc, traits::NEXT_PC);
|
gen_set_pc(tu, pc, traits::NEXT_PC);
|
||||||
tu.open_scope();<%instr.behavior.eachLine{%>
|
tu.open_scope();
|
||||||
${it}<%}%>
|
<%instr.behavior.eachLine{%>${it}
|
||||||
|
<%}%>
|
||||||
tu.close_scope();
|
tu.close_scope();
|
||||||
|
gen_trap_check(tu);
|
||||||
vm_base<ARCH>::gen_sync(tu, POST_SYNC,${idx});
|
vm_base<ARCH>::gen_sync(tu, POST_SYNC,${idx});
|
||||||
gen_trap_check(tu);
|
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
<%}%>
|
<%}%>
|
||||||
@ -227,11 +188,64 @@ private:
|
|||||||
vm_impl::gen_trap_check(tu);
|
vm_impl::gen_trap_check(tu);
|
||||||
return BRANCH;
|
return BRANCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//decoding functionality
|
||||||
|
|
||||||
|
void populate_decoding_tree(decoding_tree_node* root){
|
||||||
|
//create submask
|
||||||
|
for(auto instr: root->instrs){
|
||||||
|
root->submask &= instr.mask;
|
||||||
|
}
|
||||||
|
//put each instr according to submask&encoding into children
|
||||||
|
for(auto instr: root->instrs){
|
||||||
|
bool foundMatch = false;
|
||||||
|
for(auto child: root->children){
|
||||||
|
//use value as identifying trait
|
||||||
|
if(child->value == (instr.value&root->submask)){
|
||||||
|
child->instrs.push_back(instr);
|
||||||
|
foundMatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!foundMatch){
|
||||||
|
decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask);
|
||||||
|
child->instrs.push_back(instr);
|
||||||
|
root->children.push_back(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root->instrs.clear();
|
||||||
|
//call populate_decoding_tree for all children
|
||||||
|
if(root->children.size() >1)
|
||||||
|
for(auto child: root->children){
|
||||||
|
populate_decoding_tree(child);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//sort instrs by value of the mask, this works bc we want to have the least restrictive one last
|
||||||
|
std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) {
|
||||||
|
return instr1.mask > instr2.mask;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compile_func decode_instr(decoding_tree_node* node, code_word_t word){
|
||||||
|
if(!node->children.size()){
|
||||||
|
if(node->instrs.size() == 1) return node->instrs[0].op;
|
||||||
|
for(auto instr : node->instrs){
|
||||||
|
if((instr.mask&word) == instr.value) return instr.op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for(auto child : node->children){
|
||||||
|
if (child->value == (node->submask&word)){
|
||||||
|
return decode_instr(child, word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
|
template <typename CODE_WORD> void debug_fn(CODE_WORD instr) {
|
||||||
volatile CODE_WORD x = insn;
|
volatile CODE_WORD x = instr;
|
||||||
insn = 2 * x;
|
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()); }
|
||||||
@ -239,14 +253,11 @@ template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
|
|||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
|
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
|
||||||
: vm_base<ARCH>(core, core_id, cluster_id) {
|
: vm_base<ARCH>(core, core_id, cluster_id) {
|
||||||
qlut[0] = lut_00.data();
|
root = new decoding_tree_node(std::numeric_limits<uint32_t>::max());
|
||||||
qlut[1] = lut_01.data();
|
for(auto instr:instr_descr){
|
||||||
qlut[2] = lut_10.data();
|
root->instrs.push_back(instr);
|
||||||
qlut[3] = lut_11.data();
|
|
||||||
for (auto instr : instr_descr) {
|
|
||||||
auto quantrant = instr.value & 0x3;
|
|
||||||
expand_bit_mask(29, lutmasks[quantrant], instr.value >> 2, instr.mask >> 2, 0, qlut[quantrant], instr.op);
|
|
||||||
}
|
}
|
||||||
|
populate_decoding_tree(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
@ -254,11 +265,11 @@ std::tuple<continuation_e>
|
|||||||
vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, tu_builder& tu) {
|
vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, tu_builder& tu) {
|
||||||
// we fetch at max 4 byte, alignment is 2
|
// we fetch at max 4 byte, alignment is 2
|
||||||
enum {TRAP_ID=1<<16};
|
enum {TRAP_ID=1<<16};
|
||||||
code_word_t insn = 0;
|
code_word_t instr = 0;
|
||||||
// const typename traits::addr_t upper_bits = ~traits::PGMASK;
|
|
||||||
phys_addr_t paddr(pc);
|
phys_addr_t paddr(pc);
|
||||||
auto *const data = (uint8_t *)&insn;
|
if(this->core.has_mmu())
|
||||||
paddr = this->core.v2p(pc);
|
paddr = this->core.virt2phys(pc);
|
||||||
|
//TODO: re-add page handling
|
||||||
// if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
|
// if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
|
||||||
// auto res = this->core.read(paddr, 2, data);
|
// auto res = this->core.read(paddr, 2, data);
|
||||||
// if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
|
// if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
|
||||||
@ -266,18 +277,17 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt,
|
|||||||
// res = this->core.read(this->core.v2p(pc + 2), 2, data + 2);
|
// res = this->core.read(this->core.v2p(pc + 2), 2, data + 2);
|
||||||
// }
|
// }
|
||||||
// } else {
|
// } else {
|
||||||
auto res = this->core.read(paddr, 4, data);
|
auto res = this->core.read(paddr, 4, reinterpret_cast<uint8_t*>(&instr));
|
||||||
if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
|
if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
|
||||||
// }
|
// }
|
||||||
if (insn == 0x0000006f || (insn&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
|
if (instr == 0x0000006f || (instr&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
|
||||||
// curr pc on stack
|
// curr pc on stack
|
||||||
++inst_cnt;
|
++inst_cnt;
|
||||||
auto lut_val = extract_fields(insn);
|
auto f = decode_instr(root, instr);
|
||||||
auto f = qlut[insn & 0x3][lut_val];
|
|
||||||
if (f == nullptr) {
|
if (f == nullptr) {
|
||||||
f = &this_class::illegal_intruction;
|
f = &this_class::illegal_intruction;
|
||||||
}
|
}
|
||||||
return (this->*f)(pc, insn, tu);
|
return (this->*f)(pc, instr, tu);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -296,12 +306,13 @@ template <typename ARCH> void vm_impl<ARCH>::gen_wait(tu_builder& tu, unsigned t
|
|||||||
|
|
||||||
template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(tu_builder& tu) {
|
template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(tu_builder& tu) {
|
||||||
tu("trap_entry:");
|
tu("trap_entry:");
|
||||||
|
this->gen_sync(tu, POST_SYNC, -1);
|
||||||
tu("enter_trap(core_ptr, *trap_state, *pc, 0);");
|
tu("enter_trap(core_ptr, *trap_state, *pc, 0);");
|
||||||
tu.store(traits::LAST_BRANCH, tu.constant(std::numeric_limits<uint32_t>::max(),32));
|
tu.store(traits::LAST_BRANCH, tu.constant(std::numeric_limits<uint32_t>::max(),32));
|
||||||
tu("return *next_pc;");
|
tu("return *next_pc;");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mnrv32
|
} // namespace ${coreDef.name.toLowerCase()}
|
||||||
|
|
||||||
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) {
|
||||||
@ -317,7 +328,7 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD
|
|||||||
#include <iss/arch/riscv_hart_mu_p.h>
|
#include <iss/arch/riscv_hart_mu_p.h>
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace {
|
namespace {
|
||||||
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*) -> 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);
|
||||||
@ -333,8 +344,3 @@ std::array<bool, 2> dummy = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extern "C" {
|
|
||||||
bool* get_${coreDef.name.toLowerCase()}_tcc_creators() {
|
|
||||||
return iss::dummy.data();
|
|
||||||
}
|
|
||||||
}
|
|
3
src-gen/.gitignore
vendored
3
src-gen/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/iss
|
/iss
|
||||||
/vm
|
/vm
|
||||||
|
/sysc
|
@ -304,9 +304,9 @@ protected:
|
|||||||
*/
|
*/
|
||||||
const std::string core_type_name() const override { return traits<BASE>::core_type; }
|
const std::string core_type_name() const override { return traits<BASE>::core_type; }
|
||||||
|
|
||||||
uint64_t get_pc() override { return arch.reg.PC; };
|
uint64_t get_pc() override { return arch.reg.PC; }
|
||||||
|
|
||||||
uint64_t get_next_pc() override { return arch.reg.NEXT_PC; };
|
uint64_t get_next_pc() override { return arch.reg.NEXT_PC; }
|
||||||
|
|
||||||
uint64_t get_instr_word() override { return arch.reg.instruction; }
|
uint64_t get_instr_word() override { return arch.reg.instruction; }
|
||||||
|
|
||||||
@ -316,9 +316,11 @@ protected:
|
|||||||
|
|
||||||
uint64_t get_total_cycles() override { return arch.reg.icount + arch.cycle_offset; }
|
uint64_t get_total_cycles() override { return arch.reg.icount + arch.cycle_offset; }
|
||||||
|
|
||||||
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; };
|
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; }
|
||||||
|
|
||||||
bool is_branch_taken() override { return arch.reg.last_branch; };
|
bool is_branch_taken() override { return arch.reg.last_branch; }
|
||||||
|
|
||||||
|
unsigned get_reg_num() override {return traits<BASE>::NUM_REGS;}
|
||||||
|
|
||||||
riscv_hart_m_p<BASE, FEAT> &arch;
|
riscv_hart_m_p<BASE, FEAT> &arch;
|
||||||
};
|
};
|
||||||
@ -666,7 +668,7 @@ iss::status riscv_hart_m_p<BASE, FEAT>::read(const address_type type, const acce
|
|||||||
fault_data=addr;
|
fault_data=addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
auto phys_addr = type==iss::address_type::PHYSICAL?phys_addr_t{access, space, addr}:BASE::v2p(iss::addr_t{access, type, space, addr});
|
phys_addr_t phys_addr{access, space, addr};
|
||||||
auto res = iss::Err;
|
auto res = iss::Err;
|
||||||
if(access != access_type::FETCH && memfn_range.size()){
|
if(access != access_type::FETCH && memfn_range.size()){
|
||||||
auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){
|
auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){
|
||||||
@ -759,7 +761,7 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write(const address_type type, const acc
|
|||||||
fault_data=addr;
|
fault_data=addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
auto phys_addr = type==iss::address_type::PHYSICAL?phys_addr_t{access, space, addr}:BASE::v2p(iss::addr_t{access, type, space, addr});
|
phys_addr_t phys_addr{access, space, addr};
|
||||||
auto res = iss::Err;
|
auto res = iss::Err;
|
||||||
if(access != access_type::FETCH && memfn_range.size()){
|
if(access != access_type::FETCH && memfn_range.size()){
|
||||||
auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){
|
auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){
|
||||||
@ -784,9 +786,8 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write(const address_type type, const acc
|
|||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
phys_addr_t paddr = BASE::v2p(iss::addr_t{access, type, space, addr});
|
if ((addr + length) > mem.size()) return iss::Err;
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
switch (addr) {
|
||||||
switch (paddr.val) {
|
|
||||||
case 0x10013000: // UART0 base, TXFIFO reg
|
case 0x10013000: // UART0 base, TXFIFO reg
|
||||||
case 0x10023000: // UART1 base, TXFIFO reg
|
case 0x10023000: // UART1 base, TXFIFO reg
|
||||||
uart_buf << (char)data[0];
|
uart_buf << (char)data[0];
|
||||||
@ -798,16 +799,16 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write(const address_type type, const acc
|
|||||||
}
|
}
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
case 0x10008000: { // HFROSC base, hfrosccfg reg
|
case 0x10008000: { // HFROSC base, hfrosccfg reg
|
||||||
auto &p = mem(paddr.val / mem.page_size);
|
auto &p = mem(addr / mem.page_size);
|
||||||
auto offs = paddr.val & mem.page_addr_mask;
|
auto offs = addr & mem.page_addr_mask;
|
||||||
std::copy(data, data + length, p.data() + offs);
|
std::copy(data, data + length, p.data() + offs);
|
||||||
auto &x = *(p.data() + offs + 3);
|
auto &x = *(p.data() + offs + 3);
|
||||||
if (x & 0x40) x |= 0x80; // hfroscrdy = 1 if hfroscen==1
|
if (x & 0x40) x |= 0x80; // hfroscrdy = 1 if hfroscen==1
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
case 0x10008008: { // HFROSC base, pllcfg reg
|
case 0x10008008: { // HFROSC base, pllcfg reg
|
||||||
auto &p = mem(paddr.val / mem.page_size);
|
auto &p = mem(addr / mem.page_size);
|
||||||
auto offs = paddr.val & mem.page_addr_mask;
|
auto offs = addr & mem.page_addr_mask;
|
||||||
std::copy(data, data + length, p.data() + offs);
|
std::copy(data, data + length, p.data() + offs);
|
||||||
auto &x = *(p.data() + offs + 3);
|
auto &x = *(p.data() + offs + 3);
|
||||||
x |= 0x80; // set pll lock upon writing
|
x |= 0x80; // set pll lock upon writing
|
||||||
@ -1114,8 +1115,10 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned le
|
|||||||
}
|
}
|
||||||
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) {
|
||||||
|
@ -331,9 +331,9 @@ protected:
|
|||||||
*/
|
*/
|
||||||
const std::string core_type_name() const override { return traits<BASE>::core_type; }
|
const std::string core_type_name() const override { return traits<BASE>::core_type; }
|
||||||
|
|
||||||
uint64_t get_pc() override { return arch.reg.PC; };
|
uint64_t get_pc() override { return arch.reg.PC; }
|
||||||
|
|
||||||
uint64_t get_next_pc() override { return arch.reg.NEXT_PC; };
|
uint64_t get_next_pc() override { return arch.reg.NEXT_PC; }
|
||||||
|
|
||||||
uint64_t get_instr_word() override { return arch.reg.instruction; }
|
uint64_t get_instr_word() override { return arch.reg.instruction; }
|
||||||
|
|
||||||
@ -343,9 +343,11 @@ protected:
|
|||||||
|
|
||||||
uint64_t get_total_cycles() override { return arch.reg.icount + arch.cycle_offset; }
|
uint64_t get_total_cycles() override { return arch.reg.icount + arch.cycle_offset; }
|
||||||
|
|
||||||
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; };
|
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; }
|
||||||
|
|
||||||
bool is_branch_taken() override { return arch.reg.last_branch; };
|
bool is_branch_taken() override { return arch.reg.last_branch; }
|
||||||
|
|
||||||
|
unsigned get_reg_num() override {return traits<BASE>::NUM_REGS; }
|
||||||
|
|
||||||
riscv_hart_msu_vp<BASE> &arch;
|
riscv_hart_msu_vp<BASE> &arch;
|
||||||
};
|
};
|
||||||
@ -430,6 +432,7 @@ template <typename BASE>
|
|||||||
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
||||||
: state()
|
: state()
|
||||||
, instr_if(*this) {
|
, instr_if(*this) {
|
||||||
|
this->_has_mmu = true;
|
||||||
// reset values
|
// reset values
|
||||||
csr[misa] = traits<BASE>::MISA_VAL;
|
csr[misa] = traits<BASE>::MISA_VAL;
|
||||||
csr[mvendorid] = 0x669;
|
csr[mvendorid] = 0x669;
|
||||||
@ -632,9 +635,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto res = type==iss::address_type::PHYSICAL?
|
auto res = read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
|
||||||
read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data):
|
|
||||||
read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
|
|
||||||
if (unlikely(res != iss::Ok)){
|
if (unlikely(res != iss::Ok)){
|
||||||
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
|
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
|
||||||
fault_data=addr;
|
fault_data=addr;
|
||||||
@ -719,6 +720,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
|||||||
this->reg.trap_state = (1 << 31); // issue trap 0
|
this->reg.trap_state = (1 << 31); // issue trap 0
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
|
phys_addr_t paddr = BASE::v2p(iss::addr_t{access, type, space, addr});
|
||||||
try {
|
try {
|
||||||
if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
|
if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
|
||||||
vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp);
|
vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp);
|
||||||
@ -731,9 +733,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto res = type==iss::address_type::PHYSICAL?
|
auto res = write_mem(paddr, length, data);
|
||||||
write_mem(phys_addr_t{access, space, addr}, length, data):
|
|
||||||
write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
|
|
||||||
if (unlikely(res != iss::Ok)) {
|
if (unlikely(res != iss::Ok)) {
|
||||||
this->reg.trap_state = (1UL << 31) | (7UL << 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;
|
||||||
@ -745,7 +745,6 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
|||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
phys_addr_t paddr = BASE::v2p(iss::addr_t{access, type, space, addr});
|
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
if ((paddr.val + length) > mem.size()) return iss::Err;
|
||||||
switch (paddr.val) {
|
switch (paddr.val) {
|
||||||
case 0x10013000: // UART0 base, TXFIFO reg
|
case 0x10013000: // UART0 base, TXFIFO reg
|
||||||
|
@ -329,9 +329,9 @@ protected:
|
|||||||
*/
|
*/
|
||||||
const std::string core_type_name() const override { return traits<BASE>::core_type; }
|
const std::string core_type_name() const override { return traits<BASE>::core_type; }
|
||||||
|
|
||||||
uint64_t get_pc() override { return arch.reg.PC; };
|
uint64_t get_pc() override { return arch.reg.PC; }
|
||||||
|
|
||||||
uint64_t get_next_pc() override { return arch.reg.NEXT_PC; };
|
uint64_t get_next_pc() override { return arch.reg.NEXT_PC; }
|
||||||
|
|
||||||
uint64_t get_instr_word() override { return arch.reg.instruction; }
|
uint64_t get_instr_word() override { return arch.reg.instruction; }
|
||||||
|
|
||||||
@ -341,9 +341,11 @@ protected:
|
|||||||
|
|
||||||
uint64_t get_total_cycles() override { return arch.reg.icount + arch.cycle_offset; }
|
uint64_t get_total_cycles() override { return arch.reg.icount + arch.cycle_offset; }
|
||||||
|
|
||||||
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; };
|
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; }
|
||||||
|
|
||||||
bool is_branch_taken() override { return arch.reg.last_branch; };
|
bool is_branch_taken() override { return arch.reg.last_branch; }
|
||||||
|
|
||||||
|
unsigned get_reg_num() override {return traits<BASE>::NUM_REGS; }
|
||||||
|
|
||||||
riscv_hart_mu_p<BASE, FEAT> &arch;
|
riscv_hart_mu_p<BASE, FEAT> &arch;
|
||||||
};
|
};
|
||||||
@ -834,7 +836,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc
|
|||||||
fault_data=addr;
|
fault_data=addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
auto phys_addr = type==iss::address_type::PHYSICAL?phys_addr_t{access, space, addr}:BASE::v2p(iss::addr_t{access, type, space, addr});
|
phys_addr_t phys_addr{access, space, addr};
|
||||||
auto res = iss::Err;
|
auto res = iss::Err;
|
||||||
if(!is_fetch(access) && memfn_range.size()){
|
if(!is_fetch(access) && memfn_range.size()){
|
||||||
auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){
|
auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){
|
||||||
@ -935,7 +937,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac
|
|||||||
fault_data=addr;
|
fault_data=addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
auto phys_addr = type==iss::address_type::PHYSICAL?phys_addr_t{access, space, addr}:BASE::v2p(iss::addr_t{access, type, space, addr});
|
phys_addr_t phys_addr{access, space, addr};
|
||||||
auto res = iss::Err;
|
auto res = iss::Err;
|
||||||
if(!is_fetch(access) && memfn_range.size()){
|
if(!is_fetch(access) && memfn_range.size()){
|
||||||
auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){
|
auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){
|
||||||
@ -960,30 +962,29 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac
|
|||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
phys_addr_t paddr = BASE::v2p(iss::addr_t{access, type, space, addr});
|
if ((addr + length) > mem.size()) return iss::Err;
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
switch (addr) {
|
||||||
switch (paddr.val) {
|
|
||||||
case 0x10013000: // UART0 base, TXFIFO reg
|
case 0x10013000: // UART0 base, TXFIFO reg
|
||||||
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
|
// LOG(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("");
|
||||||
}
|
}
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
case 0x10008000: { // HFROSC base, hfrosccfg reg
|
case 0x10008000: { // HFROSC base, hfrosccfg reg
|
||||||
auto &p = mem(paddr.val / mem.page_size);
|
auto &p = mem(addr / mem.page_size);
|
||||||
auto offs = paddr.val & mem.page_addr_mask;
|
auto offs = addr & mem.page_addr_mask;
|
||||||
std::copy(data, data + length, p.data() + offs);
|
std::copy(data, data + length, p.data() + offs);
|
||||||
auto &x = *(p.data() + offs + 3);
|
auto &x = *(p.data() + offs + 3);
|
||||||
if (x & 0x40) x |= 0x80; // hfroscrdy = 1 if hfroscen==1
|
if (x & 0x40) x |= 0x80; // hfroscrdy = 1 if hfroscen==1
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
case 0x10008008: { // HFROSC base, pllcfg reg
|
case 0x10008008: { // HFROSC base, pllcfg reg
|
||||||
auto &p = mem(paddr.val / mem.page_size);
|
auto &p = mem(addr / mem.page_size);
|
||||||
auto offs = paddr.val & mem.page_addr_mask;
|
auto offs = addr & mem.page_addr_mask;
|
||||||
std::copy(data, data + length, p.data() + offs);
|
std::copy(data, data + length, p.data() + offs);
|
||||||
auto &x = *(p.data() + offs + 3);
|
auto &x = *(p.data() + offs + 3);
|
||||||
x |= 0x80; // set pll lock upon writing
|
x |= 0x80; // set pll lock upon writing
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
*
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
#include "tgc_c.h"
|
#include "tgc5c.h"
|
||||||
#include "util/ities.h"
|
#include "util/ities.h"
|
||||||
#include <util/logging.h>
|
#include <util/logging.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@ -39,18 +39,18 @@
|
|||||||
|
|
||||||
using namespace iss::arch;
|
using namespace iss::arch;
|
||||||
|
|
||||||
constexpr std::array<const char*, 36> iss::arch::traits<iss::arch::tgc_c>::reg_names;
|
constexpr std::array<const char*, 36> iss::arch::traits<iss::arch::tgc5c>::reg_names;
|
||||||
constexpr std::array<const char*, 36> iss::arch::traits<iss::arch::tgc_c>::reg_aliases;
|
constexpr std::array<const char*, 36> iss::arch::traits<iss::arch::tgc5c>::reg_aliases;
|
||||||
constexpr std::array<const uint32_t, 43> iss::arch::traits<iss::arch::tgc_c>::reg_bit_widths;
|
constexpr std::array<const uint32_t, 43> iss::arch::traits<iss::arch::tgc5c>::reg_bit_widths;
|
||||||
constexpr std::array<const uint32_t, 43> iss::arch::traits<iss::arch::tgc_c>::reg_byte_offsets;
|
constexpr std::array<const uint32_t, 43> iss::arch::traits<iss::arch::tgc5c>::reg_byte_offsets;
|
||||||
|
|
||||||
tgc_c::tgc_c() = default;
|
tgc5c::tgc5c() = default;
|
||||||
|
|
||||||
tgc_c::~tgc_c() = default;
|
tgc5c::~tgc5c() = default;
|
||||||
|
|
||||||
void tgc_c::reset(uint64_t address) {
|
void tgc5c::reset(uint64_t address) {
|
||||||
auto base_ptr = reinterpret_cast<traits<tgc_c>::reg_t*>(get_regs_base_ptr());
|
auto base_ptr = reinterpret_cast<traits<tgc5c>::reg_t*>(get_regs_base_ptr());
|
||||||
for(size_t i=0; i<traits<tgc_c>::NUM_REGS; ++i)
|
for(size_t i=0; i<traits<tgc5c>::NUM_REGS; ++i)
|
||||||
*(base_ptr+i)=0;
|
*(base_ptr+i)=0;
|
||||||
reg.PC=address;
|
reg.PC=address;
|
||||||
reg.NEXT_PC=reg.PC;
|
reg.NEXT_PC=reg.PC;
|
||||||
@ -59,11 +59,11 @@ void tgc_c::reset(uint64_t address) {
|
|||||||
reg.icount=0;
|
reg.icount=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *tgc_c::get_regs_base_ptr() {
|
uint8_t *tgc5c::get_regs_base_ptr() {
|
||||||
return reinterpret_cast<uint8_t*>(®);
|
return reinterpret_cast<uint8_t*>(®);
|
||||||
}
|
}
|
||||||
|
|
||||||
tgc_c::phys_addr_t tgc_c::virt2phys(const iss::addr_t &pc) {
|
tgc5c::phys_addr_t tgc5c::virt2phys(const iss::addr_t &addr) {
|
||||||
return phys_addr_t(pc); // change logical address to physical address
|
return phys_addr_t(addr.access, addr.space, addr.val&traits<tgc5c>::addr_mask);
|
||||||
}
|
}
|
||||||
|
|
@ -30,8 +30,8 @@
|
|||||||
*
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
#ifndef _TGC_C_H_
|
#ifndef _TGC5C_H_
|
||||||
#define _TGC_C_H_
|
#define _TGC5C_H_
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <iss/arch/traits.h>
|
#include <iss/arch/traits.h>
|
||||||
@ -41,19 +41,19 @@
|
|||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
namespace arch {
|
||||||
|
|
||||||
struct tgc_c;
|
struct tgc5c;
|
||||||
|
|
||||||
template <> struct traits<tgc_c> {
|
template <> struct traits<tgc5c> {
|
||||||
|
|
||||||
constexpr static char const* const core_type = "TGC_C";
|
constexpr static char const* const core_type = "TGC5C";
|
||||||
|
|
||||||
static constexpr std::array<const char*, 36> reg_names{
|
static constexpr std::array<const char*, 36> reg_names{
|
||||||
{"X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27", "X28", "X29", "X30", "X31", "PC", "NEXT_PC", "PRIV", "DPC"}};
|
{"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "pc", "next_pc", "priv", "dpc"}};
|
||||||
|
|
||||||
static constexpr std::array<const char*, 36> reg_aliases{
|
static constexpr std::array<const char*, 36> reg_aliases{
|
||||||
{"ZERO", "RA", "SP", "GP", "TP", "T0", "T1", "T2", "S0", "S1", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10", "S11", "T3", "T4", "T5", "T6", "PC", "NEXT_PC", "PRIV", "DPC"}};
|
{"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", "pc", "next_pc", "priv", "dpc"}};
|
||||||
|
|
||||||
enum constants {MISA_VAL=1073746180, MARCHID_VAL=2147483651, XLEN=32, INSTR_ALIGNMENT=2, RFS=32, fence=0, fencei=1, fencevmal=2, fencevmau=3, CSR_SIZE=4096, MUL_LEN=64};
|
enum constants {MISA_VAL=1073746180ULL, MARCHID_VAL=2147483651ULL, XLEN=32ULL, INSTR_ALIGNMENT=2ULL, RFS=32ULL, fence=0ULL, fencei=1ULL, fencevmal=2ULL, fencevmau=3ULL, CSR_SIZE=4096ULL, MUL_LEN=64ULL};
|
||||||
|
|
||||||
constexpr static unsigned FP_REGS_SIZE = 0;
|
constexpr static unsigned FP_REGS_SIZE = 0;
|
||||||
|
|
||||||
@ -141,49 +141,49 @@ template <> struct traits<tgc_c> {
|
|||||||
DIVU = 54,
|
DIVU = 54,
|
||||||
REM = 55,
|
REM = 55,
|
||||||
REMU = 56,
|
REMU = 56,
|
||||||
CADDI4SPN = 57,
|
C__ADDI4SPN = 57,
|
||||||
CLW = 58,
|
C__LW = 58,
|
||||||
CSW = 59,
|
C__SW = 59,
|
||||||
CADDI = 60,
|
C__ADDI = 60,
|
||||||
CNOP = 61,
|
C__NOP = 61,
|
||||||
CJAL = 62,
|
C__JAL = 62,
|
||||||
CLI = 63,
|
C__LI = 63,
|
||||||
CLUI = 64,
|
C__LUI = 64,
|
||||||
CADDI16SP = 65,
|
C__ADDI16SP = 65,
|
||||||
__reserved_clui = 66,
|
__reserved_clui = 66,
|
||||||
CSRLI = 67,
|
C__SRLI = 67,
|
||||||
CSRAI = 68,
|
C__SRAI = 68,
|
||||||
CANDI = 69,
|
C__ANDI = 69,
|
||||||
CSUB = 70,
|
C__SUB = 70,
|
||||||
CXOR = 71,
|
C__XOR = 71,
|
||||||
COR = 72,
|
C__OR = 72,
|
||||||
CAND = 73,
|
C__AND = 73,
|
||||||
CJ = 74,
|
C__J = 74,
|
||||||
CBEQZ = 75,
|
C__BEQZ = 75,
|
||||||
CBNEZ = 76,
|
C__BNEZ = 76,
|
||||||
CSLLI = 77,
|
C__SLLI = 77,
|
||||||
CLWSP = 78,
|
C__LWSP = 78,
|
||||||
CMV = 79,
|
C__MV = 79,
|
||||||
CJR = 80,
|
C__JR = 80,
|
||||||
__reserved_cmv = 81,
|
__reserved_cmv = 81,
|
||||||
CADD = 82,
|
C__ADD = 82,
|
||||||
CJALR = 83,
|
C__JALR = 83,
|
||||||
CEBREAK = 84,
|
C__EBREAK = 84,
|
||||||
CSWSP = 85,
|
C__SWSP = 85,
|
||||||
DII = 86,
|
DII = 86,
|
||||||
MAX_OPCODE
|
MAX_OPCODE
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tgc_c: public arch_if {
|
struct tgc5c: public arch_if {
|
||||||
|
|
||||||
using virt_addr_t = typename traits<tgc_c>::virt_addr_t;
|
using virt_addr_t = typename traits<tgc5c>::virt_addr_t;
|
||||||
using phys_addr_t = typename traits<tgc_c>::phys_addr_t;
|
using phys_addr_t = typename traits<tgc5c>::phys_addr_t;
|
||||||
using reg_t = typename traits<tgc_c>::reg_t;
|
using reg_t = typename traits<tgc5c>::reg_t;
|
||||||
using addr_t = typename traits<tgc_c>::addr_t;
|
using addr_t = typename traits<tgc5c>::addr_t;
|
||||||
|
|
||||||
tgc_c();
|
tgc5c();
|
||||||
~tgc_c();
|
~tgc5c();
|
||||||
|
|
||||||
void reset(uint64_t address=0) override;
|
void reset(uint64_t address=0) override;
|
||||||
|
|
||||||
@ -195,14 +195,6 @@ struct tgc_c: public arch_if {
|
|||||||
|
|
||||||
inline uint64_t stop_code() { return interrupt_sim; }
|
inline uint64_t stop_code() { return interrupt_sim; }
|
||||||
|
|
||||||
inline phys_addr_t v2p(const iss::addr_t& addr){
|
|
||||||
if (addr.space != traits<tgc_c>::MEM || addr.type == iss::address_type::PHYSICAL ||
|
|
||||||
addr_mode[static_cast<uint16_t>(addr.access)&0x3]==address_type::PHYSICAL) {
|
|
||||||
return phys_addr_t(addr.access, addr.space, addr.val&traits<tgc_c>::addr_mask);
|
|
||||||
} else
|
|
||||||
return virt2phys(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual phys_addr_t virt2phys(const iss::addr_t& addr);
|
virtual phys_addr_t virt2phys(const iss::addr_t& addr);
|
||||||
|
|
||||||
virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; }
|
virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; }
|
||||||
@ -211,7 +203,7 @@ struct tgc_c: public arch_if {
|
|||||||
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct TGC_C_regs {
|
struct TGC5C_regs {
|
||||||
uint32_t X0 = 0;
|
uint32_t X0 = 0;
|
||||||
uint32_t X1 = 0;
|
uint32_t X1 = 0;
|
||||||
uint32_t X2 = 0;
|
uint32_t X2 = 0;
|
||||||
@ -267,4 +259,4 @@ struct tgc_c: public arch_if {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* _TGC_C_H_ */
|
#endif /* _TGC5C_H_ */
|
@ -1,175 +0,0 @@
|
|||||||
#include "tgc_c.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <array>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace iss {
|
|
||||||
namespace arch {
|
|
||||||
namespace {
|
|
||||||
// according to
|
|
||||||
// https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation
|
|
||||||
#ifdef __GCC__
|
|
||||||
constexpr size_t bit_count(uint32_t u) { return __builtin_popcount(u); }
|
|
||||||
#elif __cplusplus < 201402L
|
|
||||||
constexpr size_t uCount(uint32_t u) { return u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); }
|
|
||||||
constexpr size_t bit_count(uint32_t u) { return ((uCount(u) + (uCount(u) >> 3)) & 030707070707) % 63; }
|
|
||||||
#else
|
|
||||||
constexpr size_t bit_count(uint32_t u) {
|
|
||||||
size_t uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
|
|
||||||
return ((uCount + (uCount >> 3)) & 030707070707) % 63;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using opcode_e = traits<tgc_c>::opcode_e;
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* start opcode definitions
|
|
||||||
****************************************************************************/
|
|
||||||
struct instruction_desriptor {
|
|
||||||
size_t length;
|
|
||||||
uint32_t value;
|
|
||||||
uint32_t mask;
|
|
||||||
opcode_e op;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::array<instruction_desriptor, 90> instr_descr = {{
|
|
||||||
/* entries are: size, valid value, valid mask, function ptr */
|
|
||||||
{32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, opcode_e::LUI},
|
|
||||||
{32, 0b00000000000000000000000000010111, 0b00000000000000000000000001111111, opcode_e::AUIPC},
|
|
||||||
{32, 0b00000000000000000000000001101111, 0b00000000000000000000000001111111, opcode_e::JAL},
|
|
||||||
{32, 0b00000000000000000000000001100111, 0b00000000000000000111000001111111, opcode_e::JALR},
|
|
||||||
{32, 0b00000000000000000000000001100011, 0b00000000000000000111000001111111, opcode_e::BEQ},
|
|
||||||
{32, 0b00000000000000000001000001100011, 0b00000000000000000111000001111111, opcode_e::BNE},
|
|
||||||
{32, 0b00000000000000000100000001100011, 0b00000000000000000111000001111111, opcode_e::BLT},
|
|
||||||
{32, 0b00000000000000000101000001100011, 0b00000000000000000111000001111111, opcode_e::BGE},
|
|
||||||
{32, 0b00000000000000000110000001100011, 0b00000000000000000111000001111111, opcode_e::BLTU},
|
|
||||||
{32, 0b00000000000000000111000001100011, 0b00000000000000000111000001111111, opcode_e::BGEU},
|
|
||||||
{32, 0b00000000000000000000000000000011, 0b00000000000000000111000001111111, opcode_e::LB},
|
|
||||||
{32, 0b00000000000000000001000000000011, 0b00000000000000000111000001111111, opcode_e::LH},
|
|
||||||
{32, 0b00000000000000000010000000000011, 0b00000000000000000111000001111111, opcode_e::LW},
|
|
||||||
{32, 0b00000000000000000100000000000011, 0b00000000000000000111000001111111, opcode_e::LBU},
|
|
||||||
{32, 0b00000000000000000101000000000011, 0b00000000000000000111000001111111, opcode_e::LHU},
|
|
||||||
{32, 0b00000000000000000000000000100011, 0b00000000000000000111000001111111, opcode_e::SB},
|
|
||||||
{32, 0b00000000000000000001000000100011, 0b00000000000000000111000001111111, opcode_e::SH},
|
|
||||||
{32, 0b00000000000000000010000000100011, 0b00000000000000000111000001111111, opcode_e::SW},
|
|
||||||
{32, 0b00000000000000000000000000010011, 0b00000000000000000111000001111111, opcode_e::ADDI},
|
|
||||||
{32, 0b00000000000000000010000000010011, 0b00000000000000000111000001111111, opcode_e::SLTI},
|
|
||||||
{32, 0b00000000000000000011000000010011, 0b00000000000000000111000001111111, opcode_e::SLTIU},
|
|
||||||
{32, 0b00000000000000000100000000010011, 0b00000000000000000111000001111111, opcode_e::XORI},
|
|
||||||
{32, 0b00000000000000000110000000010011, 0b00000000000000000111000001111111, opcode_e::ORI},
|
|
||||||
{32, 0b00000000000000000111000000010011, 0b00000000000000000111000001111111, opcode_e::ANDI},
|
|
||||||
{32, 0b00000000000000000001000000010011, 0b11111110000000000111000001111111, opcode_e::SLLI},
|
|
||||||
{32, 0b00000000000000000101000000010011, 0b11111110000000000111000001111111, opcode_e::SRLI},
|
|
||||||
{32, 0b01000000000000000101000000010011, 0b11111110000000000111000001111111, opcode_e::SRAI},
|
|
||||||
{32, 0b00000000000000000000000000110011, 0b11111110000000000111000001111111, opcode_e::ADD},
|
|
||||||
{32, 0b01000000000000000000000000110011, 0b11111110000000000111000001111111, opcode_e::SUB},
|
|
||||||
{32, 0b00000000000000000001000000110011, 0b11111110000000000111000001111111, opcode_e::SLL},
|
|
||||||
{32, 0b00000000000000000010000000110011, 0b11111110000000000111000001111111, opcode_e::SLT},
|
|
||||||
{32, 0b00000000000000000011000000110011, 0b11111110000000000111000001111111, opcode_e::SLTU},
|
|
||||||
{32, 0b00000000000000000100000000110011, 0b11111110000000000111000001111111, opcode_e::XOR},
|
|
||||||
{32, 0b00000000000000000101000000110011, 0b11111110000000000111000001111111, opcode_e::SRL},
|
|
||||||
{32, 0b01000000000000000101000000110011, 0b11111110000000000111000001111111, opcode_e::SRA},
|
|
||||||
{32, 0b00000000000000000110000000110011, 0b11111110000000000111000001111111, opcode_e::OR},
|
|
||||||
{32, 0b00000000000000000111000000110011, 0b11111110000000000111000001111111, opcode_e::AND},
|
|
||||||
{32, 0b00000000000000000000000000001111, 0b00000000000000000111000001111111, opcode_e::FENCE},
|
|
||||||
{32, 0b00000000000000000000000001110011, 0b11111111111111111111111111111111, opcode_e::ECALL},
|
|
||||||
{32, 0b00000000000100000000000001110011, 0b11111111111111111111111111111111, opcode_e::EBREAK},
|
|
||||||
{32, 0b00000000001000000000000001110011, 0b11111111111111111111111111111111, opcode_e::URET},
|
|
||||||
{32, 0b00010000001000000000000001110011, 0b11111111111111111111111111111111, opcode_e::SRET},
|
|
||||||
{32, 0b00110000001000000000000001110011, 0b11111111111111111111111111111111, opcode_e::MRET},
|
|
||||||
{32, 0b00010000010100000000000001110011, 0b11111111111111111111111111111111, opcode_e::WFI},
|
|
||||||
{32, 0b01111011001000000000000001110011, 0b11111111111111111111111111111111, opcode_e::DRET},
|
|
||||||
{32, 0b00000000000000000001000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRW},
|
|
||||||
{32, 0b00000000000000000010000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRS},
|
|
||||||
{32, 0b00000000000000000011000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRC},
|
|
||||||
{32, 0b00000000000000000101000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRWI},
|
|
||||||
{32, 0b00000000000000000110000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRSI},
|
|
||||||
{32, 0b00000000000000000111000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRCI},
|
|
||||||
{32, 0b00000000000000000001000000001111, 0b00000000000000000111000001111111, opcode_e::FENCE_I},
|
|
||||||
{32, 0b00000010000000000000000000110011, 0b11111110000000000111000001111111, opcode_e::MUL},
|
|
||||||
{32, 0b00000010000000000001000000110011, 0b11111110000000000111000001111111, opcode_e::MULH},
|
|
||||||
{32, 0b00000010000000000010000000110011, 0b11111110000000000111000001111111, opcode_e::MULHSU},
|
|
||||||
{32, 0b00000010000000000011000000110011, 0b11111110000000000111000001111111, opcode_e::MULHU},
|
|
||||||
{32, 0b00000010000000000100000000110011, 0b11111110000000000111000001111111, opcode_e::DIV},
|
|
||||||
{32, 0b00000010000000000101000000110011, 0b11111110000000000111000001111111, opcode_e::DIVU},
|
|
||||||
{32, 0b00000010000000000110000000110011, 0b11111110000000000111000001111111, opcode_e::REM},
|
|
||||||
{32, 0b00000010000000000111000000110011, 0b11111110000000000111000001111111, opcode_e::REMU},
|
|
||||||
{16, 0b0000000000000000, 0b1110000000000011, opcode_e::CADDI4SPN},
|
|
||||||
{16, 0b0100000000000000, 0b1110000000000011, opcode_e::CLW},
|
|
||||||
{16, 0b1100000000000000, 0b1110000000000011, opcode_e::CSW},
|
|
||||||
{16, 0b0000000000000001, 0b1110000000000011, opcode_e::CADDI},
|
|
||||||
{16, 0b0000000000000001, 0b1110111110000011, opcode_e::CNOP},
|
|
||||||
{16, 0b0010000000000001, 0b1110000000000011, opcode_e::CJAL},
|
|
||||||
{16, 0b0100000000000001, 0b1110000000000011, opcode_e::CLI},
|
|
||||||
{16, 0b0110000000000001, 0b1110000000000011, opcode_e::CLUI},
|
|
||||||
{16, 0b0110000100000001, 0b1110111110000011, opcode_e::CADDI16SP},
|
|
||||||
{16, 0b0110000000000001, 0b1111000001111111, opcode_e::__reserved_clui},
|
|
||||||
{16, 0b1000000000000001, 0b1111110000000011, opcode_e::CSRLI},
|
|
||||||
{16, 0b1000010000000001, 0b1111110000000011, opcode_e::CSRAI},
|
|
||||||
{16, 0b1000100000000001, 0b1110110000000011, opcode_e::CANDI},
|
|
||||||
{16, 0b1000110000000001, 0b1111110001100011, opcode_e::CSUB},
|
|
||||||
{16, 0b1000110000100001, 0b1111110001100011, opcode_e::CXOR},
|
|
||||||
{16, 0b1000110001000001, 0b1111110001100011, opcode_e::COR},
|
|
||||||
{16, 0b1000110001100001, 0b1111110001100011, opcode_e::CAND},
|
|
||||||
{16, 0b1010000000000001, 0b1110000000000011, opcode_e::CJ},
|
|
||||||
{16, 0b1100000000000001, 0b1110000000000011, opcode_e::CBEQZ},
|
|
||||||
{16, 0b1110000000000001, 0b1110000000000011, opcode_e::CBNEZ},
|
|
||||||
{16, 0b0000000000000010, 0b1111000000000011, opcode_e::CSLLI},
|
|
||||||
{16, 0b0100000000000010, 0b1110000000000011, opcode_e::CLWSP},
|
|
||||||
{16, 0b1000000000000010, 0b1111000000000011, opcode_e::CMV},
|
|
||||||
{16, 0b1000000000000010, 0b1111000001111111, opcode_e::CJR},
|
|
||||||
{16, 0b1000000000000010, 0b1111111111111111, opcode_e::__reserved_cmv},
|
|
||||||
{16, 0b1001000000000010, 0b1111000000000011, opcode_e::CADD},
|
|
||||||
{16, 0b1001000000000010, 0b1111000001111111, opcode_e::CJALR},
|
|
||||||
{16, 0b1001000000000010, 0b1111111111111111, opcode_e::CEBREAK},
|
|
||||||
{16, 0b1100000000000010, 0b1110000000000011, opcode_e::CSWSP},
|
|
||||||
{16, 0b0000000000000000, 0b1111111111111111, opcode_e::DII},
|
|
||||||
}};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct instruction_decoder<tgc_c> {
|
|
||||||
using opcode_e = traits<tgc_c>::opcode_e;
|
|
||||||
using code_word_t=traits<tgc_c>::code_word_t;
|
|
||||||
|
|
||||||
struct instruction_pattern {
|
|
||||||
uint32_t value;
|
|
||||||
uint32_t mask;
|
|
||||||
opcode_e id;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<std::vector<instruction_pattern>, 4> qlut;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
unsigned decode_instruction(T);
|
|
||||||
|
|
||||||
instruction_decoder() {
|
|
||||||
for (auto instr : instr_descr) {
|
|
||||||
auto quadrant = instr.value & 0x3;
|
|
||||||
qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
|
|
||||||
}
|
|
||||||
for(auto& lut: qlut){
|
|
||||||
std::sort(std::begin(lut), std::end(lut), [](instruction_pattern const& a, instruction_pattern const& b){
|
|
||||||
return bit_count(a.mask) > bit_count(b.mask);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
unsigned instruction_decoder<tgc_c>::decode_instruction<traits<tgc_c>::code_word_t>(traits<tgc_c>::code_word_t instr){
|
|
||||||
auto res = std::find_if(std::begin(qlut[instr&0x3]), std::end(qlut[instr&0x3]), [instr](instruction_pattern const& e){
|
|
||||||
return !((instr&e.mask) ^ e.value );
|
|
||||||
});
|
|
||||||
return static_cast<unsigned>(res!=std::end(qlut[instr&0x3])? res->id : opcode_e::MAX_OPCODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<instruction_decoder<tgc_c>> traits<tgc_c>::get_decoder(){
|
|
||||||
return std::make_unique<instruction_decoder<tgc_c>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,49 +2,49 @@
|
|||||||
#define _ISS_ARCH_TGC_MAPPER_H
|
#define _ISS_ARCH_TGC_MAPPER_H
|
||||||
|
|
||||||
#include "riscv_hart_m_p.h"
|
#include "riscv_hart_m_p.h"
|
||||||
#include "tgc_c.h"
|
#include "tgc5c.h"
|
||||||
using tgc_c_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_c>;
|
using tgc5c_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc5c>;
|
||||||
#ifdef CORE_TGC_A
|
#ifdef CORE_TGC5A
|
||||||
#include "riscv_hart_m_p.h"
|
#include "riscv_hart_m_p.h"
|
||||||
#include <iss/arch/tgc_a.h>
|
#include <iss/arch/tgc5a.h>
|
||||||
using tgc_a_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_a>;
|
using tgc5a_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc5a>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_B
|
#ifdef CORE_TGC5B
|
||||||
#include "riscv_hart_m_p.h"
|
#include "riscv_hart_m_p.h"
|
||||||
#include <iss/arch/tgc_b.h>
|
#include <iss/arch/tgc5b.h>
|
||||||
using tgc_b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_b>;
|
using tgc5b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc5b>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_C_XRB_NN
|
#ifdef CORE_TGC5C_XRB_NN
|
||||||
#include "riscv_hart_m_p.h"
|
#include "riscv_hart_m_p.h"
|
||||||
#include "hwl.h"
|
#include "hwl.h"
|
||||||
#include <iss/arch/tgc_c_xrb_nn.h>
|
#include <iss/arch/tgc5c_xrb_nn.h>
|
||||||
using tgc_c_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_m_p<iss::arch::tgc_c_xrb_nn>>;
|
using tgc5c_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_m_p<iss::arch::tgc5c_xrb_nn>>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_D
|
#ifdef CORE_TGC5D
|
||||||
#include "riscv_hart_mu_p.h"
|
#include "riscv_hart_mu_p.h"
|
||||||
#include <iss/arch/tgc_d.h>
|
#include <iss/arch/tgc5d.h>
|
||||||
using tgc_d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
|
using tgc5d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_D_XRB_MAC
|
#ifdef CORE_TGC5D_XRB_MAC
|
||||||
#include "riscv_hart_mu_p.h"
|
#include "riscv_hart_mu_p.h"
|
||||||
#include <iss/arch/tgc_d_xrb_mac.h>
|
#include <iss/arch/tgc5d_xrb_mac.h>
|
||||||
using tgc_d_xrb_mac_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d_xrb_mac, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
|
using tgc5d_xrb_mac_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_mac, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_D_XRB_NN
|
#ifdef CORE_TGC5D_XRB_NN
|
||||||
#include "riscv_hart_mu_p.h"
|
#include "riscv_hart_mu_p.h"
|
||||||
#include "hwl.h"
|
#include "hwl.h"
|
||||||
#include <iss/arch/tgc_d_xrb_nn.h>
|
#include <iss/arch/tgc5d_xrb_nn.h>
|
||||||
using tgc_d_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc_d_xrb_nn, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>>;
|
using tgc5d_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_nn, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_E
|
#ifdef CORE_TGC5E
|
||||||
#include "riscv_hart_mu_p.h"
|
#include "riscv_hart_mu_p.h"
|
||||||
#include <iss/arch/tgc_e.h>
|
#include <iss/arch/tgc5e.h>
|
||||||
using tgc_e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_e, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
|
using tgc5e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5e, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_X
|
#ifdef CORE_TGC5X
|
||||||
#include "riscv_hart_mu_p.h"
|
#include "riscv_hart_mu_p.h"
|
||||||
#include <iss/arch/tgc_x.h>
|
#include <iss/arch/tgc5x.h>
|
||||||
using tgc_x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_x, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N | iss::arch::FEAT_TCM)>;
|
using tgc5x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5x, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N | iss::arch::FEAT_TCM)>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -80,9 +80,17 @@ class core_factory {
|
|||||||
public:
|
public:
|
||||||
static core_factory & instance() { static core_factory bf; return bf; }
|
static core_factory & instance() { static core_factory bf; return bf; }
|
||||||
|
|
||||||
bool register_creator(const std::string &, create_fn const&);
|
bool register_creator(const std::string & className, create_fn const& fn) {
|
||||||
|
registry[className] = fn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
base_t create(const std::string &, unsigned gdb_port=0, void* init_data=nullptr) const;
|
base_t create(std::string const& className, unsigned gdb_port=0, void* init_data=nullptr) const {
|
||||||
|
registry_t::const_iterator regEntry = registry.find(className);
|
||||||
|
if (regEntry != registry.end())
|
||||||
|
return regEntry->second(gdb_port, init_data);
|
||||||
|
return {nullptr, nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> get_names() {
|
std::vector<std::string> get_names() {
|
||||||
std::vector<std::string> keys{registry.size()};
|
std::vector<std::string> keys{registry.size()};
|
||||||
@ -93,18 +101,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool core_factory::register_creator(const std::string & className, create_fn const& fn) {
|
|
||||||
registry[className] = fn;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline core_factory::base_t core_factory::create(const std::string &className, unsigned gdb_port, void* data) const {
|
|
||||||
registry_t::const_iterator regEntry = registry.find(className);
|
|
||||||
if (regEntry != registry.end())
|
|
||||||
return regEntry->second(gdb_port, data);
|
|
||||||
return {nullptr, nullptr};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _ISS_FACTORY_H_ */
|
#endif /* _ISS_FACTORY_H_ */
|
||||||
|
8
src/iss/plugin/README.md
Normal file
8
src/iss/plugin/README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# pctrace
|
||||||
|
|
||||||
|
Trace functionality to allow visualizing coverage in lcov and cachegrind tools. Use environment variables NOCOMPRES and REGDUMP to toggle functionality.
|
||||||
|
- NOCOMPRES: any value turns off the LZ4 compression
|
||||||
|
- REGDUMP: any value switches to tracing the registers instead. Also turns off compression.
|
||||||
|
|
||||||
|
Known Bugs:
|
||||||
|
- currently does not work correctly with jit backends, the plugin cant tell if instructions are compressed. Additionaly the cost of instrs that raise a trap is not known. It takes the cost of the instrid -1 (0 at the moment).
|
@ -29,7 +29,7 @@
|
|||||||
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* alex.com - initial implementation
|
* alex@minres.com - initial implementation
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include <iss/arch_if.h>
|
#include <iss/arch_if.h>
|
||||||
@ -55,11 +55,11 @@ using namespace rapidjson;
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#ifdef WITH_LZ4
|
#ifdef WITH_LZ4
|
||||||
class lz4compress_steambuf: public std::streambuf {
|
class lz4compress_streambuf: public std::streambuf {
|
||||||
public:
|
public:
|
||||||
lz4compress_steambuf(const lz4compress_steambuf&) = delete;
|
lz4compress_streambuf(const lz4compress_streambuf&) = delete;
|
||||||
lz4compress_steambuf& operator=(const lz4compress_steambuf&) = delete;
|
lz4compress_streambuf& operator=(const lz4compress_streambuf&) = delete;
|
||||||
lz4compress_steambuf(std::ostream &sink, size_t buf_size)
|
lz4compress_streambuf(std::ostream &sink, size_t buf_size)
|
||||||
: sink(sink)
|
: sink(sink)
|
||||||
, src_buf(buf_size)
|
, src_buf(buf_size)
|
||||||
, dest_buf(LZ4F_compressBound(buf_size, nullptr))
|
, dest_buf(LZ4F_compressBound(buf_size, nullptr))
|
||||||
@ -74,7 +74,7 @@ public:
|
|||||||
sink.write(dest_buf.data(), ret);
|
sink.write(dest_buf.data(), ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
~lz4compress_steambuf() {
|
~lz4compress_streambuf() {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,17 +126,21 @@ private:
|
|||||||
pctrace::pctrace(std::string const &filename)
|
pctrace::pctrace(std::string const &filename)
|
||||||
: instr_if(nullptr)
|
: instr_if(nullptr)
|
||||||
, filename(filename)
|
, filename(filename)
|
||||||
|
, reg_dump(getenv("REGDUMP"))
|
||||||
|
, no_compres(getenv("NOCOMPRES"))
|
||||||
, output("output.trc")
|
, output("output.trc")
|
||||||
#ifdef WITH_LZ4
|
#ifdef WITH_LZ4
|
||||||
, strbuf(new lz4compress_steambuf(output, 4096))
|
, strbuf( (no_compres || reg_dump)? nullptr: new lz4compress_streambuf(output, 4096))
|
||||||
, ostr(strbuf.get())
|
, ostr(strbuf.get())
|
||||||
#endif
|
#endif
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
pctrace::~pctrace() { }
|
pctrace::~pctrace() { }
|
||||||
|
|
||||||
bool pctrace::registration(const char *const version, vm_if& vm) {
|
bool pctrace::registration(const char *const version, vm_if& vm) {
|
||||||
instr_if = vm.get_arch()->get_instrumentation_if();
|
instr_if = vm.get_arch()->get_instrumentation_if();
|
||||||
|
reg_base_ptr = reinterpret_cast<uint32_t*>(vm.get_arch()->get_regs_base_ptr());
|
||||||
|
|
||||||
if(!instr_if) return false;
|
if(!instr_if) return false;
|
||||||
const string core_name = instr_if->core_type_name();
|
const string core_name = instr_if->core_type_name();
|
||||||
if (filename.length() > 0) {
|
if (filename.length() > 0) {
|
||||||
@ -203,12 +207,22 @@ void pctrace::callback(instr_info_t iinfo) {
|
|||||||
if (entry.not_taken > 1)
|
if (entry.not_taken > 1)
|
||||||
instr_if->update_last_instr_cycles(entry.not_taken);
|
instr_if->update_last_instr_cycles(entry.not_taken);
|
||||||
}
|
}
|
||||||
#ifndef WITH_LZ4
|
if(no_compres|| reg_dump){
|
||||||
output<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay <<"," << call<<","<<(compressed?2:4) <<"\n";
|
if(reg_dump){
|
||||||
#else
|
for(size_t i=0; i< instr_if->get_reg_num(); ++i){
|
||||||
auto rdbuf=ostr.rdbuf();
|
uint32_t reg_val = *(reg_base_ptr+i);
|
||||||
ostr<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay <<"," << call<<","<<(compressed?2:4) <<"\n";
|
output << "0x" << std::setfill('0') << std::setw(8) << std::hex << reg_val << " ";
|
||||||
#endif
|
}
|
||||||
}
|
output<<"\n" ;
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
output<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay <<"," << call<<","<<(compressed?2:4) <<std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
ostr<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay <<"," << call<<","<<(compressed?2:4) <<"\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}//namespace plugin
|
||||||
|
}//namespace iss
|
||||||
|
@ -37,14 +37,12 @@
|
|||||||
|
|
||||||
#include <iss/vm_plugin.h>
|
#include <iss/vm_plugin.h>
|
||||||
#include "iss/instrumentation_if.h"
|
#include "iss/instrumentation_if.h"
|
||||||
#include <json/json.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace plugin {
|
namespace plugin {
|
||||||
class lz4compress_steambuf;
|
class lz4compress_streambuf;
|
||||||
class pctrace : public iss::vm_plugin {
|
class pctrace : public iss::vm_plugin {
|
||||||
struct instr_delay {
|
struct instr_delay {
|
||||||
std::string instr_name;
|
std::string instr_name;
|
||||||
@ -87,9 +85,13 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
iss::instrumentation_if *instr_if {nullptr};
|
iss::instrumentation_if *instr_if {nullptr};
|
||||||
|
uint32_t* reg_base_ptr {nullptr};
|
||||||
|
bool reg_dump {false};
|
||||||
|
bool no_compres {false};
|
||||||
std::ofstream output;
|
std::ofstream output;
|
||||||
|
|
||||||
#ifdef WITH_LZ4
|
#ifdef WITH_LZ4
|
||||||
std::unique_ptr<lz4compress_steambuf> strbuf;
|
std::unique_ptr<lz4compress_streambuf> strbuf;
|
||||||
std::ostream ostr;
|
std::ostream ostr;
|
||||||
#endif
|
#endif
|
||||||
std::string filename;
|
std::string filename;
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include "iss/arch/tgc_mapper.h"
|
#include "iss/arch/tgc_mapper.h"
|
||||||
#ifdef WITH_LLVM
|
#ifdef WITH_LLVM
|
||||||
#include <iss/llvm/jit_helper.h>
|
#include <iss/llvm/jit_init.h>
|
||||||
#endif
|
#endif
|
||||||
#include <iss/log_categories.h>
|
#include <iss/log_categories.h>
|
||||||
#include "iss/plugin/cycle_estimate.h"
|
#include "iss/plugin/cycle_estimate.h"
|
||||||
@ -74,7 +74,7 @@ int main(int argc, char *argv[]) {
|
|||||||
("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, tcc")
|
||||||
("isa", po::value<std::string>()->default_value("tgc_c"), "isa to use for simulation");
|
("isa", po::value<std::string>()->default_value("tgc5c"), "isa to use for simulation");
|
||||||
// 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();
|
||||||
try {
|
try {
|
||||||
|
@ -37,10 +37,11 @@
|
|||||||
#include <iss/debugger/target_adapter_if.h>
|
#include <iss/debugger/target_adapter_if.h>
|
||||||
#include <iss/iss.h>
|
#include <iss/iss.h>
|
||||||
#include <iss/vm_types.h>
|
#include <iss/vm_types.h>
|
||||||
|
#include "iss_factory.h"
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#include <iss/plugin/loader.h>
|
#include <iss/plugin/loader.h>
|
||||||
#endif
|
#endif
|
||||||
#include "core_complex.h"
|
#include "sc_core_adapter_if.h"
|
||||||
#include <iss/arch/tgc_mapper.h>
|
#include <iss/arch/tgc_mapper.h>
|
||||||
#include <scc/report.h>
|
#include <scc/report.h>
|
||||||
#include <util/ities.h>
|
#include <util/ities.h>
|
||||||
@ -85,136 +86,9 @@ using namespace sc_core;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
iss::debugger::encoder_decoder encdec;
|
iss::debugger::encoder_decoder encdec;
|
||||||
|
|
||||||
std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename PLAT>
|
|
||||||
class core_wrapper_t : public PLAT {
|
|
||||||
public:
|
|
||||||
using reg_t = typename arch::traits<typename PLAT::core>::reg_t;
|
|
||||||
using phys_addr_t = typename arch::traits<typename PLAT::core>::phys_addr_t;
|
|
||||||
using heart_state_t = typename PLAT::hart_state_type;
|
|
||||||
core_wrapper_t(core_complex *owner)
|
|
||||||
: owner(owner) { }
|
|
||||||
|
|
||||||
uint32_t get_mode() { return this->reg.PRIV; }
|
|
||||||
|
|
||||||
inline void set_interrupt_execution(bool v) { this->interrupt_sim = v?1:0; }
|
|
||||||
|
|
||||||
inline bool get_interrupt_execution() { return this->interrupt_sim; }
|
|
||||||
|
|
||||||
heart_state_t &get_state() { return this->state; }
|
|
||||||
|
|
||||||
void notify_phase(iss::arch_if::exec_phase p) override {
|
|
||||||
if (p == iss::arch_if::ISTART)
|
|
||||||
owner->sync(this->instr_if.get_total_cycles());
|
|
||||||
}
|
|
||||||
|
|
||||||
sync_type needed_sync() const override { return PRE_SYNC; }
|
|
||||||
|
|
||||||
void disass_output(uint64_t pc, const std::string instr) override {
|
|
||||||
if (!owner->disass_output(pc, instr)) {
|
|
||||||
std::stringstream s;
|
|
||||||
s << "[p:" << lvl[this->reg.PRIV] << ";s:0x" << std::hex << std::setfill('0')
|
|
||||||
<< std::setw(sizeof(reg_t) * 2) << (reg_t)this->state.mstatus << std::dec << ";c:"
|
|
||||||
<< this->reg.icount + this->cycle_offset << "]";
|
|
||||||
SCCDEBUG(owner->name())<<"disass: "
|
|
||||||
<< "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40)
|
|
||||||
<< std::setfill(' ') << std::left << instr << s.str();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data) override {
|
|
||||||
if (addr.access && access_type::DEBUG)
|
|
||||||
return owner->read_mem_dbg(addr.val, length, data) ? Ok : Err;
|
|
||||||
else {
|
|
||||||
return owner->read_mem(addr.val, length, data, is_fetch(addr.access)) ? Ok : Err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status write_mem(phys_addr_t addr, unsigned length, const uint8_t *const data) override {
|
|
||||||
if (addr.access && access_type::DEBUG)
|
|
||||||
return owner->write_mem_dbg(addr.val, length, data) ? Ok : Err;
|
|
||||||
else {
|
|
||||||
auto res = owner->write_mem(addr.val, length, data) ? Ok : Err;
|
|
||||||
// clear MTIP on mtimecmp write
|
|
||||||
if (addr.val == 0x2004000) {
|
|
||||||
reg_t val;
|
|
||||||
this->read_csr(arch::mip, val);
|
|
||||||
if (val & (1ULL << 7)) this->write_csr(arch::mip, val & ~(1ULL << 7));
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status read_csr(unsigned addr, reg_t &val) override {
|
|
||||||
#ifndef CWR_SYSTEMC
|
|
||||||
if((addr==arch::time || addr==arch::timeh) && owner->mtime_o.get_interface(0)){
|
|
||||||
uint64_t time_val;
|
|
||||||
bool ret = owner->mtime_o->nb_peek(time_val);
|
|
||||||
if (addr == iss::arch::time) {
|
|
||||||
val = static_cast<reg_t>(time_val);
|
|
||||||
} else if (addr == iss::arch::timeh) {
|
|
||||||
if (sizeof(reg_t) != 4) return iss::Err;
|
|
||||||
val = static_cast<reg_t>(time_val >> 32);
|
|
||||||
}
|
|
||||||
return ret?Ok:Err;
|
|
||||||
#else
|
|
||||||
if((addr==arch::time || addr==arch::timeh)){
|
|
||||||
uint64_t time_val = owner->mtime_i.read();
|
|
||||||
if (addr == iss::arch::time) {
|
|
||||||
val = static_cast<reg_t>(time_val);
|
|
||||||
} else if (addr == iss::arch::timeh) {
|
|
||||||
if (sizeof(reg_t) != 4) return iss::Err;
|
|
||||||
val = static_cast<reg_t>(time_val >> 32);
|
|
||||||
}
|
|
||||||
return Ok;
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
return PLAT::read_csr(addr, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_until(uint64_t flags) override {
|
|
||||||
SCCDEBUG(owner->name()) << "Sleeping until interrupt";
|
|
||||||
while(this->reg.pending_trap == 0 && (this->csr[arch::mip] & this->csr[arch::mie]) == 0) {
|
|
||||||
sc_core::wait(wfi_evt);
|
|
||||||
}
|
|
||||||
PLAT::wait_until(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void local_irq(short id, bool value) {
|
|
||||||
reg_t mask = 0;
|
|
||||||
switch (id) {
|
|
||||||
case 3: // SW
|
|
||||||
mask = 1 << 3;
|
|
||||||
break;
|
|
||||||
case 7: // timer
|
|
||||||
mask = 1 << 7;
|
|
||||||
break;
|
|
||||||
case 11: // external
|
|
||||||
mask = 1 << 11;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if(id>15) mask = 1 << id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (value) {
|
|
||||||
this->csr[arch::mip] |= mask;
|
|
||||||
wfi_evt.notify();
|
|
||||||
} else
|
|
||||||
this->csr[arch::mip] &= ~mask;
|
|
||||||
this->check_interrupt();
|
|
||||||
if(value)
|
|
||||||
SCCTRACE(owner->name()) << "Triggering interrupt " << id << " Pending trap: " << this->reg.pending_trap;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
core_complex *const owner;
|
|
||||||
sc_event wfi_evt;
|
|
||||||
};
|
|
||||||
|
|
||||||
int cmd_sysc(int argc, char *argv[], debugger::out_func of, debugger::data_func df,
|
int cmd_sysc(int argc, char *argv[], debugger::out_func of, debugger::data_func df,
|
||||||
debugger::target_adapter_if *tgt_adapter) {
|
debugger::target_adapter_if *tgt_adapter) {
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
@ -254,7 +128,9 @@ public:
|
|||||||
|
|
||||||
void reset(uint64_t addr){vm->reset(addr);}
|
void reset(uint64_t addr){vm->reset(addr);}
|
||||||
inline void start(){vm->start();}
|
inline void start(){vm->start();}
|
||||||
inline std::pair<uint64_t, bool> load_file(std::string const& name){ return cpu->load_file(name);};
|
inline std::pair<uint64_t, bool> load_file(std::string const& name){
|
||||||
|
iss::arch_if* cc = cpu->get_arch_if();
|
||||||
|
return cc->load_file(name);};
|
||||||
|
|
||||||
std::function<unsigned(void)> get_mode;
|
std::function<unsigned(void)> get_mode;
|
||||||
std::function<uint64_t(void)> get_state;
|
std::function<uint64_t(void)> get_state;
|
||||||
@ -262,45 +138,35 @@ public:
|
|||||||
std::function<void(bool)> set_interrupt_execution;
|
std::function<void(bool)> set_interrupt_execution;
|
||||||
std::function<void(short, bool)> local_irq;
|
std::function<void(short, bool)> local_irq;
|
||||||
|
|
||||||
template<typename PLAT>
|
|
||||||
std::tuple<cpu_ptr, vm_ptr> create_core(std::string const& backend, unsigned gdb_port, uint32_t hart_id){
|
|
||||||
auto* lcpu = new core_wrapper_t<PLAT>(owner);
|
|
||||||
lcpu->set_mhartid(hart_id);
|
|
||||||
get_mode = [lcpu]() { return lcpu->get_mode(); };
|
|
||||||
get_state = [lcpu]() { return lcpu->get_state().mstatus.backing.val; };
|
|
||||||
get_interrupt_execution = [lcpu]() { return lcpu->get_interrupt_execution(); };
|
|
||||||
set_interrupt_execution = [lcpu](bool b) { return lcpu->set_interrupt_execution(b); };
|
|
||||||
local_irq = [lcpu](short s, bool b) { return lcpu->local_irq(s, b); };
|
|
||||||
if(backend == "interp")
|
|
||||||
return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(static_cast<typename PLAT::core*>(lcpu), gdb_port)}};
|
|
||||||
#ifdef WITH_LLVM
|
|
||||||
if(backend == "llvm")
|
|
||||||
return {cpu_ptr{lcpu}, vm_ptr{iss::llvm::create(lcpu, gdb_port)}};
|
|
||||||
#endif
|
|
||||||
#ifdef WITH_TCC
|
|
||||||
if(backend == "tcc")
|
|
||||||
s return {cpu_ptr{lcpu}, vm_ptr{iss::tcc::create(lcpu, gdb_port)}};
|
|
||||||
#endif
|
|
||||||
return {nullptr, nullptr};
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_cpu(std::string const& type, std::string const& backend, unsigned gdb_port, uint32_t hart_id){
|
void create_cpu(std::string const& type, std::string const& backend, unsigned gdb_port, uint32_t hart_id){
|
||||||
CREATE_CORE(tgc_c)
|
auto & f = sysc::iss_factory::instance();
|
||||||
#ifdef CORE_TGC_B
|
if(type.size()==0 || type == "?") {
|
||||||
CREATE_CORE(tgc_b)
|
std::cout<<"Available cores: "<<util::join(f.get_names(), ", ")<<std::endl;
|
||||||
#endif
|
sc_core::sc_stop();
|
||||||
#ifdef CORE_TGC_D
|
} else if (type.find('|') != std::string::npos) {
|
||||||
CREATE_CORE(tgc_d)
|
std::tie(cpu, vm) = f.create(type+"|"+backend);
|
||||||
#endif
|
} else {
|
||||||
#ifdef CORE_TGC_D_XRB_MAC
|
auto base_isa = type.substr(0, 5);
|
||||||
CREATE_CORE(tgc_d_xrb_mac)
|
if(base_isa=="tgc5d" || base_isa=="tgc5e") {
|
||||||
#endif
|
std::tie(cpu, vm) = f.create(type + "|mu_p_clic_pmp|" + backend, gdb_port);
|
||||||
#ifdef CORE_TGC_D_XRB_NN
|
} else {
|
||||||
CREATE_CORE(tgc_d_xrb_nn)
|
std::tie(cpu, vm) = f.create(type + "|m_p|" + backend, gdb_port, owner);
|
||||||
#endif
|
}
|
||||||
{
|
|
||||||
LOG(ERR) << "Illegal argument value for core type: " << type << std::endl;
|
|
||||||
}
|
}
|
||||||
|
if(!cpu ){
|
||||||
|
SCCFATAL() << "Could not create cpu for isa " << type << " and backend " <<backend;
|
||||||
|
}
|
||||||
|
if(!vm ){
|
||||||
|
SCCFATAL() << "Could not create vm for isa " << type << " and backend " <<backend;
|
||||||
|
}
|
||||||
|
auto* sc_cpu_if = reinterpret_cast<sc_core_adapter_if*>(cpu.get());
|
||||||
|
sc_cpu_if->set_mhartid(hart_id);
|
||||||
|
get_mode = [sc_cpu_if]() { return sc_cpu_if->get_mode(); };
|
||||||
|
get_state = [sc_cpu_if]() { return sc_cpu_if->get_state(); };
|
||||||
|
get_interrupt_execution = [sc_cpu_if]() { return sc_cpu_if->get_interrupt_execution(); };
|
||||||
|
set_interrupt_execution = [sc_cpu_if](bool b) { return sc_cpu_if->set_interrupt_execution(b); };
|
||||||
|
local_irq = [sc_cpu_if](short s, bool b) { return sc_cpu_if->local_irq(s, b); };
|
||||||
|
|
||||||
auto *srv = debugger::server<debugger::gdb_session>::get();
|
auto *srv = debugger::server<debugger::gdb_session>::get();
|
||||||
if (srv) tgt_adapter = srv->get_target();
|
if (srv) tgt_adapter = srv->get_target();
|
||||||
if (tgt_adapter)
|
if (tgt_adapter)
|
||||||
@ -313,7 +179,7 @@ public:
|
|||||||
|
|
||||||
core_complex * const owner;
|
core_complex * const owner;
|
||||||
vm_ptr vm{nullptr};
|
vm_ptr vm{nullptr};
|
||||||
cpu_ptr cpu{nullptr};
|
sc_cpu_ptr cpu{nullptr};
|
||||||
iss::debugger::target_adapter_if *tgt_adapter{nullptr};
|
iss::debugger::target_adapter_if *tgt_adapter{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -634,5 +500,5 @@ bool core_complex::write_mem_dbg(uint64_t addr, unsigned length, const uint8_t *
|
|||||||
gp.set_streaming_width(length);
|
gp.set_streaming_width(length);
|
||||||
return dbus->transport_dbg(gp) == length;
|
return dbus->transport_dbg(gp) == length;
|
||||||
}
|
}
|
||||||
} /* namespace SiFive */
|
} /* namespace tgfs */
|
||||||
} /* namespace sysc */
|
} /* namespace sysc */
|
||||||
|
@ -96,7 +96,7 @@ public:
|
|||||||
|
|
||||||
cci::cci_param<uint64_t> reset_address{"reset_address", 0ULL};
|
cci::cci_param<uint64_t> reset_address{"reset_address", 0ULL};
|
||||||
|
|
||||||
cci::cci_param<std::string> core_type{"core_type", "tgc_c"};
|
cci::cci_param<std::string> core_type{"core_type", "tgc5c"};
|
||||||
|
|
||||||
cci::cci_param<std::string> backend{"backend", "interp"};
|
cci::cci_param<std::string> backend{"backend", "interp"};
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ public:
|
|||||||
|
|
||||||
scml_property<unsigned long long> reset_address{"reset_address", 0ULL};
|
scml_property<unsigned long long> reset_address{"reset_address", 0ULL};
|
||||||
|
|
||||||
scml_property<std::string> core_type{"core_type", "tgc_c"};
|
scml_property<std::string> core_type{"core_type", "tgc5c"};
|
||||||
|
|
||||||
scml_property<std::string> backend{"backend", "interp"};
|
scml_property<std::string> backend{"backend", "interp"};
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ public:
|
|||||||
, elf_file{"elf_file", ""}
|
, elf_file{"elf_file", ""}
|
||||||
, enable_disass{"enable_disass", false}
|
, enable_disass{"enable_disass", false}
|
||||||
, reset_address{"reset_address", 0ULL}
|
, reset_address{"reset_address", 0ULL}
|
||||||
, core_type{"core_type", "tgc_c"}
|
, core_type{"core_type", "tgc5c"}
|
||||||
, backend{"backend", "interp"}
|
, backend{"backend", "interp"}
|
||||||
, gdb_server_port{"gdb_server_port", 0}
|
, gdb_server_port{"gdb_server_port", 0}
|
||||||
, dump_ir{"dump_ir", false}
|
, dump_ir{"dump_ir", false}
|
||||||
|
88
src/sysc/iss_factory.h
Normal file
88
src/sysc/iss_factory.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (C) 2021 MINRES Technologies GmbH
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _ISS_FACTORY_H_
|
||||||
|
#define _ISS_FACTORY_H_
|
||||||
|
|
||||||
|
#include <iss/iss.h>
|
||||||
|
#include "sc_core_adapter_if.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace sysc {
|
||||||
|
|
||||||
|
using sc_cpu_ptr = std::unique_ptr<sc_core_adapter_if>;
|
||||||
|
using vm_ptr= std::unique_ptr<iss::vm_if>;
|
||||||
|
|
||||||
|
class iss_factory {
|
||||||
|
public:
|
||||||
|
using base_t = std::tuple<sc_cpu_ptr, vm_ptr>;
|
||||||
|
using create_fn = std::function<base_t(unsigned, void*) >;
|
||||||
|
using registry_t = std::unordered_map<std::string, create_fn> ;
|
||||||
|
|
||||||
|
iss_factory() = default;
|
||||||
|
iss_factory(const iss_factory &) = delete;
|
||||||
|
iss_factory & operator=(const iss_factory &) = delete;
|
||||||
|
|
||||||
|
static iss_factory & instance() { static iss_factory bf; return bf; }
|
||||||
|
|
||||||
|
bool register_creator(const std::string & className, create_fn const& fn) {
|
||||||
|
registry[className] = fn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
base_t create(std::string const& className, unsigned gdb_port=0, void* init_data=nullptr) const {
|
||||||
|
registry_t::const_iterator regEntry = registry.find(className);
|
||||||
|
if (regEntry != registry.end())
|
||||||
|
return regEntry->second(gdb_port, init_data);
|
||||||
|
return {nullptr, nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> get_names() {
|
||||||
|
std::vector<std::string> keys{registry.size()};
|
||||||
|
std::transform(std::begin(registry), std::end(registry), std::begin(keys), [](std::pair<std::string, create_fn> const& p){
|
||||||
|
return p.first;
|
||||||
|
});
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
registry_t registry;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _ISS_FACTORY_H_ */
|
@ -1,33 +1,74 @@
|
|||||||
/*
|
/*******************************************************************************
|
||||||
* register_tgc_c.cpp
|
* Copyright (C) 2023 MINRES Technologies GmbH
|
||||||
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Created on: Jul 5, 2023
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* Author: eyck
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*/
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include "iss_factory.h"
|
||||||
|
#include <iss/arch/tgc5c.h>
|
||||||
|
|
||||||
#include <iss/factory.h>
|
|
||||||
#include <iss/arch/tgc_c.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 "sc_core_adapter.h"
|
#include "sc_core_adapter.h"
|
||||||
#include "core_complex.h"
|
#include "core_complex.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace {
|
namespace interp {
|
||||||
volatile std::array<bool, 2> dummy = {
|
using namespace sysc;
|
||||||
core_factory::instance().register_creator("tgc_c|m_p|interp", [](unsigned gdb_port, void* data) -> std::tuple<cpu_ptr, vm_ptr>{
|
volatile std::array<bool, 2> tgc_init = {
|
||||||
|
iss_factory::instance().register_creator("tgc5c|m_p|interp", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
|
||||||
auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
||||||
arch::tgc_c* lcpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::tgc_c>>(cc);
|
auto* cpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::tgc5c>>(cc);
|
||||||
return {cpu_ptr{lcpu}, vm_ptr{interp::create(lcpu, gdb_port)}};
|
return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::tgc5c*>(cpu), gdb_port)}};
|
||||||
}),
|
}),
|
||||||
core_factory::instance().register_creator("tgc_c|mu_p|interp", [](unsigned gdb_port, void* data) -> std::tuple<cpu_ptr, vm_ptr>{
|
iss_factory::instance().register_creator("tgc5c|mu_p|interp", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
|
||||||
auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
||||||
arch::tgc_c* lcpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::tgc_c>>(cc);
|
auto* cpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::tgc5c>>(cc);
|
||||||
return {cpu_ptr{lcpu}, vm_ptr{interp::create(lcpu, gdb_port)}};
|
return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::tgc5c*>(cpu), gdb_port)}};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#if defined(WITH_TCC)
|
||||||
|
namespace tcc {
|
||||||
|
using namespace sysc;
|
||||||
|
volatile std::array<bool, 2> tgc_init = {
|
||||||
|
iss_factory::instance().register_creator("tgc5c|m_p|tcc", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
|
||||||
|
auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
||||||
|
auto* cpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::tgc5c>>(cc);
|
||||||
|
return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::tgc5c*>(cpu), gdb_port)}};
|
||||||
|
}),
|
||||||
|
iss_factory::instance().register_creator("tgc5c|mu_p|tcc", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
|
||||||
|
auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
|
||||||
|
auto* cpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::tgc5c>>(cc);
|
||||||
|
return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::tgc5c*>(cpu), gdb_port)}};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
#include <scc/report.h>
|
#include <scc/report.h>
|
||||||
#include <util/ities.h>
|
#include <util/ities.h>
|
||||||
#include "core_complex.h"
|
#include "sc_core_adapter_if.h"
|
||||||
#include <iss/iss.h>
|
#include <iss/iss.h>
|
||||||
#include <iss/vm_types.h>
|
#include <iss/vm_types.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace sysc {
|
||||||
template<typename PLAT>
|
template<typename PLAT>
|
||||||
class sc_core_adapter : public PLAT {
|
class sc_core_adapter : public PLAT, public sc_core_adapter_if {
|
||||||
public:
|
public:
|
||||||
using reg_t = typename iss::arch::traits<typename PLAT::core>::reg_t;
|
using reg_t = typename iss::arch::traits<typename PLAT::core>::reg_t;
|
||||||
using phys_addr_t = typename iss::arch::traits<typename PLAT::core>::phys_addr_t;
|
using phys_addr_t = typename iss::arch::traits<typename PLAT::core>::phys_addr_t;
|
||||||
@ -26,13 +26,17 @@ public:
|
|||||||
sc_core_adapter(sysc::tgfs::core_complex *owner)
|
sc_core_adapter(sysc::tgfs::core_complex *owner)
|
||||||
: owner(owner) { }
|
: owner(owner) { }
|
||||||
|
|
||||||
uint32_t get_mode() { return this->reg.PRIV; }
|
iss::arch_if* get_arch_if() override { return this;}
|
||||||
|
|
||||||
inline void set_interrupt_execution(bool v) { this->interrupt_sim = v?1:0; }
|
void set_mhartid(unsigned id) override { PLAT::set_mhartid(id); }
|
||||||
|
|
||||||
inline bool get_interrupt_execution() { return this->interrupt_sim; }
|
uint32_t get_mode() override { return this->reg.PRIV; }
|
||||||
|
|
||||||
heart_state_t &get_state() { return this->state; }
|
void set_interrupt_execution(bool v) override { this->interrupt_sim = v?1:0; }
|
||||||
|
|
||||||
|
bool get_interrupt_execution() override { return this->interrupt_sim; }
|
||||||
|
|
||||||
|
uint64_t get_state() override { return this->state.mstatus.backing.val; }
|
||||||
|
|
||||||
void notify_phase(iss::arch_if::exec_phase p) override {
|
void notify_phase(iss::arch_if::exec_phase p) override {
|
||||||
if (p == iss::arch_if::ISTART)
|
if (p == iss::arch_if::ISTART)
|
||||||
@ -88,7 +92,7 @@ public:
|
|||||||
if (sizeof(reg_t) != 4) return iss::Err;
|
if (sizeof(reg_t) != 4) return iss::Err;
|
||||||
val = static_cast<reg_t>(time_val >> 32);
|
val = static_cast<reg_t>(time_val >> 32);
|
||||||
}
|
}
|
||||||
return ret?Ok:Err;
|
return ret?iss::Ok:iss::Err;
|
||||||
#else
|
#else
|
||||||
if((addr==iss::arch::time || addr==iss::arch::timeh)){
|
if((addr==iss::arch::time || addr==iss::arch::timeh)){
|
||||||
uint64_t time_val = owner->mtime_i.read();
|
uint64_t time_val = owner->mtime_i.read();
|
||||||
@ -113,7 +117,7 @@ public:
|
|||||||
PLAT::wait_until(flags);
|
PLAT::wait_until(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void local_irq(short id, bool value) {
|
void local_irq(short id, bool value) override {
|
||||||
reg_t mask = 0;
|
reg_t mask = 0;
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case 3: // SW
|
case 3: // SW
|
||||||
@ -141,8 +145,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
sysc::tgfs::core_complex *const owner;
|
sysc::tgfs::core_complex *const owner;
|
||||||
sc_event wfi_evt;
|
sc_core::sc_event wfi_evt;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _SYSC_SC_CORE_ADAPTER_H_ */
|
#endif /* _SYSC_SC_CORE_ADAPTER_H_ */
|
||||||
|
31
src/sysc/sc_core_adapter_if.h
Normal file
31
src/sysc/sc_core_adapter_if.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* sc_core_adapter.h
|
||||||
|
*
|
||||||
|
* Created on: Jul 5, 2023
|
||||||
|
* Author: eyck
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SYSC_SC_CORE_ADAPTER_IF_H_
|
||||||
|
#define _SYSC_SC_CORE_ADAPTER_IF_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <scc/report.h>
|
||||||
|
#include <util/ities.h>
|
||||||
|
#include "core_complex.h"
|
||||||
|
#include <iss/iss.h>
|
||||||
|
#include <iss/vm_types.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace sysc {
|
||||||
|
struct sc_core_adapter_if {
|
||||||
|
virtual iss::arch_if* get_arch_if() = 0;
|
||||||
|
virtual void set_mhartid(unsigned) = 0;
|
||||||
|
virtual uint32_t get_mode() = 0;
|
||||||
|
virtual uint64_t get_state() = 0;
|
||||||
|
virtual bool get_interrupt_execution() = 0;
|
||||||
|
virtual void set_interrupt_execution(bool v) = 0;
|
||||||
|
virtual void local_irq(short id, bool value) = 0;
|
||||||
|
virtual ~sc_core_adapter_if() = default;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif /* _SYSC_SC_CORE_ADAPTER_IF_H_ */
|
2711
src/vm/interp/vm_tgc5c.cpp
Normal file
2711
src/vm/interp/vm_tgc5c.cpp
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4561
src/vm/llvm/vm_tgc5c.cpp
Normal file
4561
src/vm/llvm/vm_tgc5c.cpp
Normal file
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
Reference in New Issue
Block a user