Compare commits
27 Commits
e432dd8208
...
msvc_compa
Author | SHA1 | Date | |
---|---|---|---|
c8679fca85 | |||
f0ada1ba8c | |||
09b01af3fa | |||
9c8b72693e | |||
c409e7b7ca | |||
2f05083cf0 | |||
e934049dd4 | |||
94f796ebdb | |||
836ba269e3 | |||
c8681096be | |||
adeffe47ad | |||
d95846a849 | |||
af887c286f | |||
4ddf50162c | |||
da819d8890 | |||
5ef5d57d30 | |||
d7bddd825c | |||
15f46a87db | |||
fc1ae4d57d | |||
d0f3a120fd | |||
c592a26346 | |||
e68918c2e8 | |||
473f8a5a17 | |||
2f4b5bd9b2 | |||
23b9741adf | |||
5d8da08ce5 | |||
a249aea703 |
170
CMakeLists.txt
170
CMakeLists.txt
@ -1,20 +1,21 @@
|
|||||||
cmake_minimum_required(VERSION 3.12)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
###############################################################################
|
||||||
project(dbt-core-tgc VERSION 1.0.0)
|
#
|
||||||
|
###############################################################################
|
||||||
|
project(dbt-rise-tgc VERSION 1.0.0)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
conan_basic_setup()
|
find_package(elfio)
|
||||||
|
|
||||||
find_package(Boost COMPONENTS program_options system thread filesystem REQUIRED)
|
|
||||||
if(WITH_LLVM)
|
if(WITH_LLVM)
|
||||||
if(DEFINED ENV{LLVM_HOME})
|
if(DEFINED ENV{LLVM_HOME})
|
||||||
find_path (LLVM_DIR LLVM-Config.cmake $ENV{LLVM_HOME}/lib/cmake/llvm)
|
find_path (LLVM_DIR LLVM-Config.cmake $ENV{LLVM_HOME}/lib/cmake/llvm)
|
||||||
endif(DEFINED ENV{LLVM_HOME})
|
endif(DEFINED ENV{LLVM_HOME})
|
||||||
find_package(LLVM REQUIRED CONFIG)
|
find_package(LLVM REQUIRED CONFIG)
|
||||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||||
llvm_map_components_to_libnames(llvm_libs support core mcjit x86codegen x86asmparser)
|
llvm_map_components_to_libnames(llvm_libs support core mcjit x86codegen x86asmparser)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#Mac needed variables (adapt for your needs - http://www.cmake.org/Wiki/CMake_RPATH_handling#Mac_OS_X_and_the_RPATH)
|
#Mac needed variables (adapt for your needs - http://www.cmake.org/Wiki/CMake_RPATH_handling#Mac_OS_X_and_the_RPATH)
|
||||||
@ -32,7 +33,7 @@ FILE(GLOB TGC_SOURCES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/vm/interp/vm_*.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/vm/interp/vm_*.cpp
|
||||||
)
|
)
|
||||||
set(LIB_SOURCES
|
set(LIB_SOURCES
|
||||||
src/vm/fp_functions.cpp
|
src/vm/fp_functions.cpp
|
||||||
src/plugin/instruction_count.cpp
|
src/plugin/instruction_count.cpp
|
||||||
src/plugin/cycle_estimate.cpp
|
src/plugin/cycle_estimate.cpp
|
||||||
${TGC_SOURCES}
|
${TGC_SOURCES}
|
||||||
@ -40,82 +41,129 @@ set(LIB_SOURCES
|
|||||||
|
|
||||||
if(WITH_LLVM)
|
if(WITH_LLVM)
|
||||||
set(LIB_SOURCES ${LIB_SOURCES}
|
set(LIB_SOURCES ${LIB_SOURCES}
|
||||||
src/vm/llvm/fp_impl.cpp
|
src/vm/llvm/fp_impl.cpp
|
||||||
#src/vm/llvm/vm_tgf_b.cpp
|
#src/vm/llvm/vm_tgf_b.cpp
|
||||||
#src/vm/llvm/vm_tgf_c.cpp
|
#src/vm/llvm/vm_tgf_c.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Define the library
|
# Define the library
|
||||||
add_library(${PROJECT_NAME} SHARED ${LIB_SOURCES})
|
add_library(${PROJECT_NAME} ${LIB_SOURCES})
|
||||||
# list code gen dependencies
|
# list code gen dependencies
|
||||||
if(TARGET ${CORE_NAME}_cpp)
|
if(TARGET ${CORE_NAME}_cpp)
|
||||||
add_dependencies(${PROJECT_NAME} ${CORE_NAME}_cpp)
|
add_dependencies(${PROJECT_NAME} ${CORE_NAME}_cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-shift-count-overflow)
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-shift-count-overflow)
|
||||||
|
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE /wd4293)
|
||||||
|
endif()
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC incl)
|
target_include_directories(${PROJECT_NAME} PUBLIC incl)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC softfloat scc-util jsoncpp)
|
target_link_libraries(${PROJECT_NAME} PUBLIC softfloat scc-util jsoncpp)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC -Wl,--whole-archive dbt-core -Wl,--no-whole-archive)
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${Boost_LIBRARIES} )
|
target_link_libraries(${PROJECT_NAME} PUBLIC -Wl,--whole-archive dbt-core -Wl,--no-whole-archive)
|
||||||
|
else()
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-core)
|
||||||
|
endif()
|
||||||
|
if(TARGET CONAN_PKG::elfio)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC CONAN_PKG::elfio)
|
||||||
|
elseif(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()
|
||||||
|
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
FRAMEWORK FALSE
|
FRAMEWORK FALSE
|
||||||
PUBLIC_HEADER "${LIB_HEADERS}" # specify the public headers
|
|
||||||
)
|
)
|
||||||
|
install(TARGETS ${PROJECT_NAME} COMPONENT ${PROJECT_NAME}
|
||||||
if(SystemC_FOUND)
|
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
|
||||||
add_library(${PROJECT_NAME}_sc src/sysc/core_complex.cpp)
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib
|
||||||
target_compile_definitions(${PROJECT_NAME}_sc PUBLIC WITH_SYSTEMC)
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib
|
||||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_b.h)
|
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac
|
||||||
target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_B)
|
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} # headers for mac (note the different component -> different package)
|
||||||
endif()
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers
|
||||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_c.h)
|
)
|
||||||
target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_C)
|
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss COMPONENT ${PROJECT_NAME}
|
||||||
endif()
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # target directory
|
||||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_d.h)
|
FILES_MATCHING # install only matched files
|
||||||
target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_D)
|
PATTERN "*.h" # select header files
|
||||||
endif()
|
)
|
||||||
target_include_directories(${PROJECT_NAME}_sc PUBLIC ../incl ${SystemC_INCLUDE_DIRS} ${CCI_INCLUDE_DIRS})
|
###############################################################################
|
||||||
|
#
|
||||||
if(SCV_FOUND)
|
###############################################################################
|
||||||
target_compile_definitions(${PROJECT_NAME}_sc PUBLIC WITH_SCV)
|
|
||||||
target_include_directories(${PROJECT_NAME}_sc PUBLIC ${SCV_INCLUDE_DIRS})
|
|
||||||
endif()
|
|
||||||
target_link_libraries(${PROJECT_NAME}_sc PUBLIC ${PROJECT_NAME} scc)
|
|
||||||
if(WITH_LLVM)
|
|
||||||
target_link_libraries(${PROJECT_NAME}_sc PUBLIC ${llvm_libs})
|
|
||||||
endif()
|
|
||||||
set_target_properties(${PROJECT_NAME}_sc PROPERTIES
|
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
FRAMEWORK FALSE
|
|
||||||
PUBLIC_HEADER "${LIB_HEADERS}" # specify the public headers
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(tgc-sim)
|
project(tgc-sim)
|
||||||
|
find_package(Boost COMPONENTS program_options thread REQUIRED)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} src/main.cpp)
|
add_executable(${PROJECT_NAME} src/main.cpp)
|
||||||
# This sets the include directory for the reference project. This is the -I flag in gcc.
|
# This sets the include directory for the reference project. This is the -I flag in gcc.
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
||||||
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()
|
||||||
# Links the target exe against the libraries
|
# Links the target exe against the libraries
|
||||||
target_link_libraries(${PROJECT_NAME} dbt-core-tgc)
|
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc)
|
||||||
target_link_libraries(${PROJECT_NAME} jsoncpp)
|
if(TARGET Boost::program_options)
|
||||||
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} )
|
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::program_options Boost::thread)
|
||||||
|
else()
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${BOOST_program_options_LIBRARY} ${BOOST_thread_LIBRARY})
|
||||||
|
endif()
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS})
|
||||||
if (Tcmalloc_FOUND)
|
if (Tcmalloc_FOUND)
|
||||||
target_link_libraries(${PROJECT_NAME} ${Tcmalloc_LIBRARIES})
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${Tcmalloc_LIBRARIES})
|
||||||
endif(Tcmalloc_FOUND)
|
endif(Tcmalloc_FOUND)
|
||||||
|
|
||||||
install(TARGETS dbt-core-tgc tgc-sim
|
install(TARGETS tgc-sim
|
||||||
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
|
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libs # static lib
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libs # binaries
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libs # shared lib
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib
|
||||||
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libs # for mac
|
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac
|
||||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT devel # 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
|
||||||
)
|
)
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
project(dbt-rise-tgc_sc VERSION 1.0.0)
|
||||||
|
|
||||||
|
include(SystemCPackage)
|
||||||
|
if(SystemC_FOUND)
|
||||||
|
add_library(${PROJECT_NAME} src/sysc/core_complex.cpp)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_SYSTEMC)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_b.h)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_TGC_B)
|
||||||
|
endif()
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_c.h)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_TGC_C)
|
||||||
|
endif()
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_d.h)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_TGC_D)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc scc)
|
||||||
|
if(WITH_LLVM)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(LIB_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/incl/sysc/core_complex.h)
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
FRAMEWORK FALSE
|
||||||
|
PUBLIC_HEADER "${LIB_HEADERS}" # specify the public headers
|
||||||
|
)
|
||||||
|
install(TARGETS ${PROJECT_NAME} COMPONENT ${PROJECT_NAME}
|
||||||
|
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # static lib
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # binaries
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # shared lib
|
||||||
|
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} # for mac
|
||||||
|
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/sysc # headers for mac (note the different component -> different package)
|
||||||
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
30
contrib/build.tcl
Normal file
30
contrib/build.tcl
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
namespace eval Specification {
|
||||||
|
proc buildproc { args } {
|
||||||
|
global env
|
||||||
|
variable installDir
|
||||||
|
variable compiler
|
||||||
|
variable compiler [::scsh::get_backend_compiler]
|
||||||
|
# set target $machine
|
||||||
|
set target [::scsh::machine]
|
||||||
|
set linkerOptions ""
|
||||||
|
set preprocessorOptions ""
|
||||||
|
set libversion $compiler
|
||||||
|
switch -exact -- $target {
|
||||||
|
"linux" {
|
||||||
|
set install_dir $::env(TGFS_INSTALL_ROOT)
|
||||||
|
set incldir "${install_dir}/include"
|
||||||
|
set libdir "${install_dir}/lib64"
|
||||||
|
set preprocessorOptions [concat $preprocessorOptions "-I${incldir}"]
|
||||||
|
# Set the Linker paths.
|
||||||
|
set linkerOptions [concat $linkerOptions "-Wl,-rpath,${libdir} -L${libdir} -ldbt-rise-tgc_sc"]
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
puts stderr "ERROR: \"$target\" is not supported, [::scsh::version]"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
::scsh::cwr_append_ipsimbld_opts preprocessor "$preprocessorOptions"
|
||||||
|
::scsh::cwr_append_ipsimbld_opts linker "$linkerOptions"
|
||||||
|
}
|
||||||
|
::scsh::add_build_callback [namespace current]::buildproc
|
||||||
|
}
|
4
contrib/tgc_import.cc
Normal file
4
contrib/tgc_import.cc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
#include "sysc/core_complex.h"
|
||||||
|
|
||||||
|
void modules() { sysc::tgfs::core_complex i_core_complex("core_complex"); }
|
50
contrib/tgc_import.tcl
Normal file
50
contrib/tgc_import.tcl
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
proc getScriptDirectory {} {
|
||||||
|
set dispScriptFile [file normalize [info script]]
|
||||||
|
set scriptFolder [file dirname $dispScriptFile]
|
||||||
|
return $scriptFolder
|
||||||
|
}
|
||||||
|
if { $::env(SNPS_VP_PRODUCT) == "PAULTRA" } {
|
||||||
|
set hardware /HARDWARE/HW/HW
|
||||||
|
} else {
|
||||||
|
set hardware /HARDWARE
|
||||||
|
}
|
||||||
|
|
||||||
|
set scriptDir [getScriptDirectory]
|
||||||
|
set top_design_name core_complex
|
||||||
|
set clocks clk_i
|
||||||
|
set resets rst_i
|
||||||
|
set model_prefix "i_"
|
||||||
|
set model_postfix ""
|
||||||
|
|
||||||
|
::pct::new_project
|
||||||
|
::pct::open_library TLM2_PL
|
||||||
|
::pct::clear_systemc_defines
|
||||||
|
::pct::clear_systemc_include_path
|
||||||
|
::pct::add_to_systemc_include_path $::env(TGFS_INSTALL_ROOT)/include
|
||||||
|
::pct::set_import_protocol_generation_flag false
|
||||||
|
::pct::set_update_existing_encaps_flag true
|
||||||
|
::pct::set_dynamic_port_arrays_flag true
|
||||||
|
::pct::set_import_scml_properties_flag true
|
||||||
|
::pct::load_modules --set-category modules tgc_import.cc
|
||||||
|
|
||||||
|
# Set Port Protocols correctly
|
||||||
|
set block ${top_design_name}
|
||||||
|
foreach clock ${clocks} {
|
||||||
|
::pct::set_block_port_protocol --set-category SYSTEM_LIBRARY:$block/${clock} SYSTEM_LIBRARY:CLOCK
|
||||||
|
}
|
||||||
|
foreach reset ${resets} {
|
||||||
|
::pct::set_block_port_protocol --set-category SYSTEM_LIBRARY:$block/${reset} SYSTEM_LIBRARY:RESET
|
||||||
|
}
|
||||||
|
::pct::set_encap_port_array_size SYSTEM_LIBRARY:$block/local_irq_i 16
|
||||||
|
|
||||||
|
# Set compile settings and look
|
||||||
|
set block SYSTEM_LIBRARY:${top_design_name}
|
||||||
|
::pct::set_encap_build_script $block/${top_design_name} $scriptDir/build.tcl
|
||||||
|
::pct::set_background_color_rgb $block 255 255 255 255
|
||||||
|
::pct::create_instance SYSTEM_LIBRARY:${top_design_name} ${hardware} ${model_prefix}${top_design_name}${model_postfix} ${top_design_name}
|
||||||
|
|
||||||
|
# export the result as component
|
||||||
|
::pct::export_system_library ${top_design_name} ${top_design_name}.xml
|
Submodule gen_input/CoreDSL-Instruction-Set-Description updated: cf601042ed...8d9a0fb149
@ -33,7 +33,7 @@
|
|||||||
def getRegisterSizes(){
|
def getRegisterSizes(){
|
||||||
def regs = registers.collect{it.size}
|
def regs = registers.collect{it.size}
|
||||||
regs[-1]=64 // correct for NEXT_PC
|
regs[-1]=64 // correct for NEXT_PC
|
||||||
regs+=[32, 32, 64] // append TRAP_STATE, PENDING_TRAP, ICOUNT
|
regs+=[32, 32, 64, 64, 64] // append TRAP_STATE, PENDING_TRAP, ICOUNT, CYCLE, INSTRET
|
||||||
return regs
|
return regs
|
||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
|
@ -37,7 +37,7 @@ def nativeTypeSize(int size){
|
|||||||
}
|
}
|
||||||
def getRegisterSizes(){
|
def getRegisterSizes(){
|
||||||
def regs = registers.collect{nativeTypeSize(it.size)}
|
def regs = registers.collect{nativeTypeSize(it.size)}
|
||||||
regs+=[32,32, 64] // append TRAP_STATE, PENDING_TRAP, ICOUNT
|
regs+=[32,32, 64, 64, 64] // append TRAP_STATE, PENDING_TRAP, ICOUNT, CYCLE, INSTRET
|
||||||
return regs
|
return regs
|
||||||
}
|
}
|
||||||
def getRegisterOffsets(){
|
def getRegisterOffsets(){
|
||||||
@ -94,7 +94,9 @@ template <> struct traits<${coreDef.name.toLowerCase()}> {
|
|||||||
${registers.collect{it.name}.join(', ')}, NUM_REGS,
|
${registers.collect{it.name}.join(', ')}, NUM_REGS,
|
||||||
TRAP_STATE=NUM_REGS,
|
TRAP_STATE=NUM_REGS,
|
||||||
PENDING_TRAP,
|
PENDING_TRAP,
|
||||||
ICOUNT
|
ICOUNT,
|
||||||
|
CYCLE,
|
||||||
|
INSTRET
|
||||||
};
|
};
|
||||||
|
|
||||||
using reg_t = uint${addrDataWidth}_t;
|
using reg_t = uint${addrDataWidth}_t;
|
||||||
@ -175,6 +177,8 @@ protected:
|
|||||||
}}%>
|
}}%>
|
||||||
uint32_t trap_state = 0, pending_trap = 0;
|
uint32_t trap_state = 0, pending_trap = 0;
|
||||||
uint64_t icount = 0;
|
uint64_t icount = 0;
|
||||||
|
uint64_t cycle = 0;
|
||||||
|
uint64_t instret = 0;
|
||||||
uint32_t last_branch;
|
uint32_t last_branch;
|
||||||
} reg;
|
} reg;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
@ -85,6 +85,7 @@ protected:
|
|||||||
|
|
||||||
inline const char *name(size_t index){return traits::reg_aliases.at(index);}
|
inline const char *name(size_t index){return traits::reg_aliases.at(index);}
|
||||||
|
|
||||||
|
compile_func decode_inst(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
|
||||||
@ -98,46 +99,13 @@ protected:
|
|||||||
std::array<compile_func, LUT_SIZE_C> lut_00, lut_01, lut_10;
|
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, LUT_SIZE> lut_11;
|
||||||
|
|
||||||
std::array<compile_func *, 4> qlut;
|
struct instruction_pattern {
|
||||||
|
uint32_t value;
|
||||||
|
uint32_t mask;
|
||||||
|
compile_func opc;
|
||||||
|
};
|
||||||
|
|
||||||
std::array<const uint32_t, 4> lutmasks = {{EXTR_MASK16, EXTR_MASK16, EXTR_MASK16, EXTR_MASK32}};
|
std::array<std::vector<instruction_pattern>, 4> qlut;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
@ -219,20 +187,22 @@ private:
|
|||||||
/* instruction ${idx}: ${instr.name} */
|
/* instruction ${idx}: ${instr.name} */
|
||||||
compile_ret_t __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr){
|
compile_ret_t __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr){
|
||||||
// pre execution stuff
|
// pre execution stuff
|
||||||
this->do_sync(PRE_SYNC, ${idx});
|
auto* PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]);
|
||||||
|
auto NEXT_PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]);
|
||||||
|
*PC=*NEXT_PC;
|
||||||
|
auto* trap_state = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::TRAP_STATE]);
|
||||||
|
*trap_state = *reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PENDING_TRAP]);
|
||||||
|
if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, ${idx});
|
||||||
<%instr.fields.eachLine{%>${it}
|
<%instr.fields.eachLine{%>${it}
|
||||||
<%}%>if(this->disass_enabled){
|
<%}%>if(this->disass_enabled){
|
||||||
/* generate console output when executing the command */
|
/* generate console output when executing the command */
|
||||||
<%instr.disass.eachLine{%>${it}
|
<%instr.disass.eachLine{%>${it}
|
||||||
<%}%>
|
<%}%>
|
||||||
}
|
}
|
||||||
// prepare execution
|
|
||||||
uint${addrDataWidth}_t* PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]);
|
|
||||||
uint${addrDataWidth}_t* NEXT_PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]);
|
|
||||||
// used registers<%instr.usedVariables.each{ k,v->
|
// used registers<%instr.usedVariables.each{ k,v->
|
||||||
if(v.isArray) {%>
|
if(v.isArray) {%>
|
||||||
uint${v.type.size}_t* ${k} = reinterpret_cast<uint${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${v.type.size}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}0]);<% }else{ %>
|
||||||
uint${v.type.size}_t* ${k} = reinterpret_cast<uint${v.type.size}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}]);
|
auto* ${k} = reinterpret_cast<uint${v.type.size}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}]);
|
||||||
<%}}%>// calculate next pc value
|
<%}}%>// calculate next pc value
|
||||||
*NEXT_PC = *PC + ${instr.length/8};
|
*NEXT_PC = *PC + ${instr.length/8};
|
||||||
// execute instruction
|
// execute instruction
|
||||||
@ -241,11 +211,14 @@ private:
|
|||||||
<%}%>} catch(...){}
|
<%}%>} catch(...){}
|
||||||
// post execution stuff
|
// post execution stuff
|
||||||
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, ${idx});
|
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, ${idx});
|
||||||
auto* trap_state = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::TRAP_STATE]);
|
|
||||||
// trap check
|
// trap check
|
||||||
if(*trap_state!=0){
|
if(*trap_state!=0){
|
||||||
super::core.enter_trap(*trap_state, pc.val);
|
super::core.enter_trap(*trap_state, pc.val, instr);
|
||||||
|
} else {
|
||||||
|
(*reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::ICOUNT]))++;
|
||||||
|
(*reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::INSTRET]))++;
|
||||||
}
|
}
|
||||||
|
(*reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::CYCLE]))++;
|
||||||
pc.val=*NEXT_PC;
|
pc.val=*NEXT_PC;
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
@ -264,7 +237,7 @@ private:
|
|||||||
auto* trap_state = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::TRAP_STATE]);
|
auto* trap_state = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::TRAP_STATE]);
|
||||||
// trap check
|
// trap check
|
||||||
if(*trap_state!=0){
|
if(*trap_state!=0){
|
||||||
super::core.enter_trap(*trap_state, pc.val);
|
super::core.enter_trap(*trap_state, pc.val, instr);
|
||||||
}
|
}
|
||||||
pc.val=*NEXT_PC;
|
pc.val=*NEXT_PC;
|
||||||
return pc;
|
return pc;
|
||||||
@ -291,26 +264,53 @@ template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
|
|||||||
|
|
||||||
template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
|
template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
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();
|
|
||||||
qlut[1] = lut_01.data();
|
|
||||||
qlut[2] = lut_10.data();
|
|
||||||
qlut[3] = lut_11.data();
|
|
||||||
for (auto instr : instr_descr) {
|
for (auto instr : instr_descr) {
|
||||||
auto quantrant = instr.value & 0x3;
|
auto quadrant = instr.value & 0x3;
|
||||||
expand_bit_mask(29, lutmasks[quantrant], instr.value >> 2, instr.mask >> 2, 0, qlut[quantrant], instr.op);
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_count_limit_enabled(finish_cond_e cond){
|
inline bool is_count_limit_enabled(finish_cond_e cond){
|
||||||
return (cond & finish_cond_e::COUNT_LIMIT) == finish_cond_e::COUNT_LIMIT;
|
return (cond & finish_cond_e::COUNT_LIMIT) == finish_cond_e::COUNT_LIMIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ARCH>
|
||||||
|
typename vm_impl<ARCH>::compile_func vm_impl<ARCH>::decode_inst(code_word_t instr){
|
||||||
|
for(auto& e: qlut[instr&0x3]){
|
||||||
|
if(!((instr&e.mask) ^ e.value )) return e.opc;
|
||||||
|
}
|
||||||
|
return &this_class::illegal_intruction;
|
||||||
|
}
|
||||||
|
|
||||||
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){
|
||||||
// we fetch at max 4 byte, alignment is 2
|
// we fetch at max 4 byte, alignment is 2
|
||||||
enum {TRAP_ID=1<<16};
|
|
||||||
code_word_t insn = 0;
|
code_word_t insn = 0;
|
||||||
auto *const data = (uint8_t *)&insn;
|
auto *const data = (uint8_t *)&insn;
|
||||||
auto pc=start;
|
auto pc=start;
|
||||||
@ -318,17 +318,14 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
|||||||
!(is_count_limit_enabled(cond) && this->core.get_icount() >= icount_limit)){
|
!(is_count_limit_enabled(cond) && this->core.get_icount() >= icount_limit)){
|
||||||
auto res = fetch_ins(pc, data);
|
auto res = fetch_ins(pc, data);
|
||||||
if(res!=iss::Ok){
|
if(res!=iss::Ok){
|
||||||
auto new_pc = super::core.enter_trap(TRAP_ID, pc.val);
|
this->do_sync(POST_SYNC, std::numeric_limits<unsigned>::max());
|
||||||
res = fetch_ins(virt_addr_t{access_type::FETCH, new_pc}, data);
|
pc.val = super::core.enter_trap(std::numeric_limits<uint64_t>::max(), pc.val, 0);
|
||||||
if(res!=iss::Ok) throw simulation_stopped(0);
|
} else {
|
||||||
|
if (is_jump_to_self_enabled(cond) &&
|
||||||
|
(insn == 0x0000006f || (insn&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
|
||||||
|
auto f = decode_inst(insn);
|
||||||
|
pc = (this->*f)(pc, insn);
|
||||||
}
|
}
|
||||||
if ((cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF &&
|
|
||||||
(insn == 0x0000006f || (insn&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
|
|
||||||
auto lut_val = extract_fields(insn);
|
|
||||||
auto f = qlut[insn & 0x3][lut_val];
|
|
||||||
if (!f)
|
|
||||||
f = &this_class::illegal_intruction;
|
|
||||||
pc = (this->*f)(pc, insn);
|
|
||||||
}
|
}
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
@ -104,12 +104,19 @@ enum riscv_csr {
|
|||||||
mie = 0x304,
|
mie = 0x304,
|
||||||
mtvec = 0x305,
|
mtvec = 0x305,
|
||||||
mcounteren = 0x306,
|
mcounteren = 0x306,
|
||||||
|
mtvt = 0x307, //CLIC
|
||||||
// Machine Trap Handling
|
// Machine Trap Handling
|
||||||
mscratch = 0x340,
|
mscratch = 0x340,
|
||||||
mepc = 0x341,
|
mepc = 0x341,
|
||||||
mcause = 0x342,
|
mcause = 0x342,
|
||||||
mtval = 0x343,
|
mtval = 0x343,
|
||||||
mip = 0x344,
|
mip = 0x344,
|
||||||
|
mxnti = 0x345, //CLIC
|
||||||
|
mintstatus = 0x346, // MRW Current interrupt levels (CLIC) - addr subject to change
|
||||||
|
mscratchcsw = 0x348, // MRW Conditional scratch swap on priv mode change (CLIC)
|
||||||
|
mscratchcswl = 0x349, // MRW Conditional scratch swap on level change (CLIC)
|
||||||
|
mintthresh = 0x350, // MRW Interrupt-level threshold (CLIC) - addr subject to change
|
||||||
|
mclicbase = 0x351, // MRW Base address for CLIC memory mapped registers (CLIC) - addr subject to change
|
||||||
// Physical Memory Protection
|
// Physical Memory Protection
|
||||||
pmpcfg0 = 0x3A0,
|
pmpcfg0 = 0x3A0,
|
||||||
pmpcfg1 = 0x3A1,
|
pmpcfg1 = 0x3A1,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (C) 2021, MINRES Technologies GmbH
|
* Copyright (C) 2021 MINRES Technologies GmbH
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -91,11 +91,11 @@ protected:
|
|||||||
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
|
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
|
||||||
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
|
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
|
||||||
public:
|
public:
|
||||||
using super = BASE;
|
using core = BASE;
|
||||||
using this_class = riscv_hart_m_p<BASE>;
|
using this_class = riscv_hart_m_p<BASE>;
|
||||||
using phys_addr_t = typename super::phys_addr_t;
|
using phys_addr_t = typename core::phys_addr_t;
|
||||||
using reg_t = typename super::reg_t;
|
using reg_t = typename core::reg_t;
|
||||||
using addr_t = typename super::addr_t;
|
using addr_t = typename core::addr_t;
|
||||||
|
|
||||||
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
||||||
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
||||||
@ -144,24 +144,40 @@ public:
|
|||||||
|
|
||||||
mstatus_t mstatus;
|
mstatus_t mstatus;
|
||||||
|
|
||||||
static const reg_t mstatus_reset_val = 0;
|
static const reg_t mstatus_reset_val = 0x1800;
|
||||||
|
|
||||||
void write_mstatus(T val) {
|
void write_mstatus(T val) {
|
||||||
auto mask = get_mask();
|
auto mask = get_mask() &0xff; // MPP is hardcode as 0x3
|
||||||
auto new_val = (mstatus.backing.val & ~mask) | (val & mask);
|
auto new_val = (mstatus.backing.val & ~mask) | (val & mask);
|
||||||
mstatus = new_val;
|
mstatus = new_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
T satp;
|
|
||||||
|
|
||||||
static constexpr uint32_t get_mask() {
|
static constexpr uint32_t get_mask() {
|
||||||
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported
|
//return 0x807ff988UL; // 0b1000 0000 0111 1111 1111 1000 1000 1000 // only machine mode is supported
|
||||||
|
// +-SD
|
||||||
|
// | +-TSR
|
||||||
|
// | |+-TW
|
||||||
|
// | ||+-TVM
|
||||||
|
// | |||+-MXR
|
||||||
|
// | ||||+-SUM
|
||||||
|
// | |||||+-MPRV
|
||||||
|
// | |||||| +-XS
|
||||||
|
// | |||||| | +-FS
|
||||||
|
// | |||||| | | +-MPP
|
||||||
|
// | |||||| | | | +-SPP
|
||||||
|
// | |||||| | | | |+-MPIE
|
||||||
|
// | ||||||/|/|/| || +-MIE
|
||||||
|
return 0b00000000000000000001100010001000;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
using hart_state_type = hart_state<reg_t>;
|
using hart_state_type = hart_state<reg_t>;
|
||||||
|
|
||||||
constexpr reg_t get_irq_mask() {
|
constexpr reg_t get_irq_mask() {
|
||||||
return 0b101110111011; // only machine mode is supported
|
return 0b100010001000; // only machine mode is supported
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reg_t get_pc_mask() {
|
||||||
|
return traits<BASE>::MISA_VAL&0b0100?~1:~3;
|
||||||
}
|
}
|
||||||
|
|
||||||
riscv_hart_m_p();
|
riscv_hart_m_p();
|
||||||
@ -176,8 +192,8 @@ public:
|
|||||||
iss::status write(const address_type type, const access_type access, const uint32_t space,
|
iss::status write(const address_type type, const access_type access, const uint32_t space,
|
||||||
const uint64_t addr, const unsigned length, const uint8_t *const data) override;
|
const uint64_t addr, const unsigned length, const uint8_t *const data) override;
|
||||||
|
|
||||||
virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_m_p::enter_trap(flags, fault_data); }
|
virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_m_p::enter_trap(flags, fault_data, fault_data); }
|
||||||
virtual uint64_t enter_trap(uint64_t flags, uint64_t addr) override;
|
virtual uint64_t enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) override;
|
||||||
virtual uint64_t leave_trap(uint64_t flags) override;
|
virtual uint64_t leave_trap(uint64_t flags) override;
|
||||||
|
|
||||||
const reg_t& get_mhartid() const { return mhartid_reg; }
|
const reg_t& get_mhartid() const { return mhartid_reg; }
|
||||||
@ -234,7 +250,10 @@ protected:
|
|||||||
virtual iss::status write_csr(unsigned addr, reg_t val);
|
virtual iss::status write_csr(unsigned addr, reg_t val);
|
||||||
|
|
||||||
hart_state_type state;
|
hart_state_type state;
|
||||||
uint64_t cycle_offset;
|
int64_t cycle_offset{0};
|
||||||
|
uint64_t mcycle_csr{0};
|
||||||
|
int64_t instret_offset{0};
|
||||||
|
uint64_t minstret_csr{0};
|
||||||
reg_t fault_data;
|
reg_t fault_data;
|
||||||
uint64_t tohost = tohost_dflt;
|
uint64_t tohost = tohost_dflt;
|
||||||
uint64_t fromhost = fromhost_dflt;
|
uint64_t fromhost = fromhost_dflt;
|
||||||
@ -255,15 +274,23 @@ protected:
|
|||||||
private:
|
private:
|
||||||
iss::status read_reg(unsigned addr, reg_t &val);
|
iss::status read_reg(unsigned addr, reg_t &val);
|
||||||
iss::status write_reg(unsigned addr, reg_t val);
|
iss::status write_reg(unsigned addr, reg_t val);
|
||||||
|
iss::status read_null(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_null(unsigned addr, reg_t val){return iss::status::Ok;}
|
||||||
iss::status read_cycle(unsigned addr, reg_t &val);
|
iss::status read_cycle(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_cycle(unsigned addr, reg_t val);
|
||||||
|
iss::status read_instret(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_instret(unsigned addr, reg_t val);
|
||||||
|
iss::status read_tvec(unsigned addr, reg_t &val);
|
||||||
iss::status read_time(unsigned addr, reg_t &val);
|
iss::status read_time(unsigned addr, reg_t &val);
|
||||||
iss::status read_status(unsigned addr, reg_t &val);
|
iss::status read_status(unsigned addr, reg_t &val);
|
||||||
iss::status write_status(unsigned addr, reg_t val);
|
iss::status write_status(unsigned addr, reg_t val);
|
||||||
|
iss::status write_cause(unsigned addr, reg_t val);
|
||||||
iss::status read_ie(unsigned addr, reg_t &val);
|
iss::status read_ie(unsigned addr, reg_t &val);
|
||||||
iss::status write_ie(unsigned addr, reg_t val);
|
iss::status write_ie(unsigned addr, reg_t val);
|
||||||
iss::status read_ip(unsigned addr, reg_t &val);
|
iss::status read_ip(unsigned addr, reg_t &val);
|
||||||
iss::status write_ip(unsigned addr, reg_t val);
|
iss::status write_ip(unsigned addr, reg_t val);
|
||||||
iss::status read_hartid(unsigned addr, reg_t &val);
|
iss::status read_hartid(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_epc(unsigned addr, reg_t val);
|
||||||
|
|
||||||
reg_t mhartid_reg{0x0};
|
reg_t mhartid_reg{0x0};
|
||||||
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
|
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
|
||||||
@ -276,37 +303,71 @@ protected:
|
|||||||
template <typename BASE>
|
template <typename BASE>
|
||||||
riscv_hart_m_p<BASE>::riscv_hart_m_p()
|
riscv_hart_m_p<BASE>::riscv_hart_m_p()
|
||||||
: state()
|
: state()
|
||||||
, cycle_offset(0)
|
|
||||||
, instr_if(*this) {
|
, instr_if(*this) {
|
||||||
|
// reset values
|
||||||
csr[misa] = traits<BASE>::MISA_VAL;
|
csr[misa] = traits<BASE>::MISA_VAL;
|
||||||
|
csr[mvendorid] = 0x669;
|
||||||
|
csr[marchid] = 0x80000003;
|
||||||
|
csr[mimpid] = 1;
|
||||||
|
|
||||||
uart_buf.str("");
|
uart_buf.str("");
|
||||||
for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr;
|
for (unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr){
|
||||||
for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr;
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
// special handling
|
csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
csr_rd_cb[time] = &riscv_hart_m_p<BASE>::read_time;
|
|
||||||
csr_wr_cb[time] = nullptr;
|
|
||||||
csr_rd_cb[timeh] = &riscv_hart_m_p<BASE>::read_time;
|
|
||||||
csr_wr_cb[timeh] = nullptr;
|
|
||||||
csr_rd_cb[mcycle] = &riscv_hart_m_p<BASE>::read_cycle;
|
|
||||||
csr_rd_cb[mcycleh] = &riscv_hart_m_p<BASE>::read_cycle;
|
|
||||||
csr_rd_cb[minstret] = &riscv_hart_m_p<BASE>::read_cycle;
|
|
||||||
csr_rd_cb[minstreth] = &riscv_hart_m_p<BASE>::read_cycle;
|
|
||||||
csr_rd_cb[mstatus] = &riscv_hart_m_p<BASE>::read_status;
|
|
||||||
csr_wr_cb[mstatus] = &riscv_hart_m_p<BASE>::write_status;
|
|
||||||
csr_rd_cb[mip] = &riscv_hart_m_p<BASE>::read_ip;
|
|
||||||
csr_wr_cb[mip] = &riscv_hart_m_p<BASE>::write_ip;
|
|
||||||
csr_rd_cb[mie] = &riscv_hart_m_p<BASE>::read_ie;
|
|
||||||
csr_wr_cb[mie] = &riscv_hart_m_p<BASE>::write_ie;
|
|
||||||
csr_rd_cb[mhartid] = &riscv_hart_m_p<BASE>::read_hartid;
|
|
||||||
// common regs
|
|
||||||
const std::array<unsigned, 6> addrs{{mepc, mtvec, mscratch, mcause, mtval, mscratch}};
|
|
||||||
for(auto addr: addrs) {
|
|
||||||
csr_rd_cb[addr] = &riscv_hart_m_p<BASE>::read_reg;
|
|
||||||
csr_wr_cb[addr] = &riscv_hart_m_p<BASE>::write_reg;
|
|
||||||
}
|
}
|
||||||
// read-only registers
|
for (unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr){
|
||||||
csr_rd_cb[misa] = &riscv_hart_m_p<BASE>::read_reg;
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
csr_wr_cb[misa] = nullptr;
|
csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
|
}
|
||||||
|
for (unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
|
}
|
||||||
|
for (unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
}
|
||||||
|
for (unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
//csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
|
}
|
||||||
|
// common regs
|
||||||
|
const std::array<unsigned, 10> addrs{{misa, mvendorid, marchid, mimpid, mepc, mtvec, mscratch, mcause, mtval, mscratch}};
|
||||||
|
for(auto addr: addrs) {
|
||||||
|
csr_rd_cb[addr] = &this_class::read_reg;
|
||||||
|
csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
|
}
|
||||||
|
// special handling & overrides
|
||||||
|
csr_rd_cb[time] = &this_class::read_time;
|
||||||
|
csr_rd_cb[timeh] = &this_class::read_time;
|
||||||
|
csr_rd_cb[cycle] = &this_class::read_cycle;
|
||||||
|
csr_rd_cb[cycleh] = &this_class::read_cycle;
|
||||||
|
csr_rd_cb[instret] = &this_class::read_instret;
|
||||||
|
csr_rd_cb[instreth] = &this_class::read_instret;
|
||||||
|
|
||||||
|
csr_rd_cb[mcycle] = &this_class::read_cycle;
|
||||||
|
csr_wr_cb[mcycle] = &this_class::write_cycle;
|
||||||
|
csr_rd_cb[mcycleh] = &this_class::read_cycle;
|
||||||
|
csr_wr_cb[mcycleh] = &this_class::write_cycle;
|
||||||
|
csr_rd_cb[minstret] = &this_class::read_instret;
|
||||||
|
csr_wr_cb[minstret] = &this_class::write_instret;
|
||||||
|
csr_rd_cb[minstreth] = &this_class::read_instret;
|
||||||
|
csr_wr_cb[minstreth] = &this_class::write_instret;
|
||||||
|
csr_rd_cb[mstatus] = &this_class::read_status;
|
||||||
|
csr_wr_cb[mstatus] = &this_class::write_status;
|
||||||
|
csr_wr_cb[mcause] = &this_class::write_cause;
|
||||||
|
csr_rd_cb[mtvec] = &this_class::read_tvec;
|
||||||
|
csr_wr_cb[mepc] = &this_class::write_epc;
|
||||||
|
csr_rd_cb[mip] = &this_class::read_ip;
|
||||||
|
csr_wr_cb[mip] = &this_class::write_ip;
|
||||||
|
csr_rd_cb[mie] = &this_class::read_ie;
|
||||||
|
csr_wr_cb[mie] = &this_class::write_ie;
|
||||||
|
csr_rd_cb[mhartid] = &this_class::read_hartid;
|
||||||
|
csr_rd_cb[mcounteren] = &this_class::read_null;
|
||||||
|
csr_wr_cb[mcounteren] = &this_class::write_null;
|
||||||
|
csr_wr_cb[misa] = &this_class::write_null;
|
||||||
|
csr_wr_cb[mvendorid] = &this_class::write_null;
|
||||||
|
csr_wr_cb[marchid] = &this_class::write_null;
|
||||||
|
csr_wr_cb[mimpid] = &this_class::write_null;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> std::pair<uint64_t, bool> riscv_hart_m_p<BASE>::load_file(std::string name, int type) {
|
template <typename BASE> std::pair<uint64_t, bool> riscv_hart_m_p<BASE>::load_file(std::string name, int type) {
|
||||||
@ -327,6 +388,7 @@ template <typename BASE> std::pair<uint64_t, bool> riscv_hart_m_p<BASE>::load_fi
|
|||||||
if (sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file");
|
if (sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file");
|
||||||
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
||||||
if (reader.get_machine() != EM_RISCV) throw std::runtime_error("wrong elf machine in file");
|
if (reader.get_machine() != EM_RISCV) throw std::runtime_error("wrong elf machine in file");
|
||||||
|
auto entry = reader.get_entry();
|
||||||
for (const auto pseg : reader.segments) {
|
for (const auto pseg : reader.segments) {
|
||||||
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
||||||
const auto seg_data = pseg->get_data();
|
const auto seg_data = pseg->get_data();
|
||||||
@ -335,18 +397,39 @@ template <typename BASE> std::pair<uint64_t, bool> riscv_hart_m_p<BASE>::load_fi
|
|||||||
traits<BASE>::MEM, pseg->get_physical_address(),
|
traits<BASE>::MEM, pseg->get_physical_address(),
|
||||||
fsize, reinterpret_cast<const uint8_t *const>(seg_data));
|
fsize, reinterpret_cast<const uint8_t *const>(seg_data));
|
||||||
if (res != iss::Ok)
|
if (res != iss::Ok)
|
||||||
LOG(ERROR) << "problem writing " << fsize << "bytes to 0x" << std::hex
|
LOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex
|
||||||
<< pseg->get_physical_address();
|
<< pseg->get_physical_address();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto sec : reader.sections) {
|
for(const auto sec : reader.sections) {
|
||||||
if (sec->get_name() == ".tohost") {
|
if(sec->get_name() == ".symtab") {
|
||||||
|
if ( SHT_SYMTAB == sec->get_type() ||
|
||||||
|
SHT_DYNSYM == sec->get_type() ) {
|
||||||
|
ELFIO::symbol_section_accessor symbols( reader, sec );
|
||||||
|
auto sym_no = symbols.get_symbols_num();
|
||||||
|
std::string name;
|
||||||
|
ELFIO::Elf64_Addr value = 0;
|
||||||
|
ELFIO::Elf_Xword size = 0;
|
||||||
|
unsigned char bind = 0;
|
||||||
|
unsigned char type = 0;
|
||||||
|
ELFIO::Elf_Half section = 0;
|
||||||
|
unsigned char other = 0;
|
||||||
|
for ( auto i = 0U; i < sym_no; ++i ) {
|
||||||
|
symbols.get_symbol( i, name, value, size, bind, type, section, other );
|
||||||
|
if(name=="tohost") {
|
||||||
|
tohost = value;
|
||||||
|
} else if(name=="fromhost") {
|
||||||
|
fromhost = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (sec->get_name() == ".tohost") {
|
||||||
tohost = sec->get_address();
|
tohost = sec->get_address();
|
||||||
fromhost = tohost + 0x40;
|
fromhost = tohost + 0x40;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(reader.get_entry(), true);
|
}
|
||||||
|
return std::make_pair(entry, true);
|
||||||
}
|
}
|
||||||
throw std::runtime_error("memory load file is not a valid elf file");
|
throw std::runtime_error("memory load file is not a valid elf file");
|
||||||
}
|
}
|
||||||
@ -375,13 +458,23 @@ iss::status riscv_hart_m_p<BASE>::read(const address_type type, const access_typ
|
|||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
auto alignment = access == iss::access_type::FETCH? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length;
|
||||||
|
if(alignment>1 && (addr&(alignment-1))){
|
||||||
|
this->reg.trap_state = 1<<31 | 4<<16;
|
||||||
|
fault_data=addr;
|
||||||
|
return iss::Err;
|
||||||
|
}
|
||||||
auto res = type==iss::address_type::PHYSICAL?
|
auto res = type==iss::address_type::PHYSICAL?
|
||||||
read_mem( BASE::v2p(phys_addr_t{access, 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);
|
read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
|
||||||
if (unlikely(res != iss::Ok)) this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
|
if (unlikely(res != iss::Ok)){
|
||||||
|
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
|
||||||
|
fault_data=addr;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -407,6 +500,7 @@ iss::status riscv_hart_m_p<BASE>::read(const address_type type, const access_typ
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -447,14 +541,22 @@ iss::status riscv_hart_m_p<BASE>::write(const address_type type, const access_ty
|
|||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
if(!(access && iss::access_type::DEBUG) && length>1 && (addr&(length-1))){
|
||||||
|
this->reg.trap_state = 1<<31 | 6<<16;
|
||||||
|
fault_data=addr;
|
||||||
|
return iss::Err;
|
||||||
|
}
|
||||||
auto res = type==iss::address_type::PHYSICAL?
|
auto res = type==iss::address_type::PHYSICAL?
|
||||||
write_mem(phys_addr_t{access, space, addr}, 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);
|
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 = (1 << 31) | (5 << 16); // issue trap 7 (Store/AMO access fault)
|
this->reg.trap_state = (1 << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)
|
||||||
|
fault_data=addr;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,6 +616,7 @@ iss::status riscv_hart_m_p<BASE>::write(const address_type type, const access_ty
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,6 +650,11 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_reg(unsigned add
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_null(unsigned addr, reg_t &val) {
|
||||||
|
val = 0;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_reg(unsigned addr, reg_t val) {
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_reg(unsigned addr, reg_t val) {
|
||||||
csr[addr] = val;
|
csr[addr] = val;
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
@ -563,8 +671,50 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_cycle(unsigned a
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cycle(unsigned addr, reg_t val) {
|
||||||
|
if (sizeof(typename traits<BASE>::reg_t) != 4) {
|
||||||
|
if (addr == mcycleh)
|
||||||
|
return iss::Err;
|
||||||
|
mcycle_csr = static_cast<uint64_t>(val);
|
||||||
|
} else {
|
||||||
|
if (addr == mcycle) {
|
||||||
|
mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val;
|
||||||
|
} else {
|
||||||
|
mcycle_csr = (static_cast<uint64_t>(val)<<32) + (mcycle_csr & 0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cycle_offset = mcycle_csr-this->reg.icount; // TODO: relying on wrap-around
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_instret(unsigned addr, reg_t &val) {
|
||||||
|
if ((addr&0xff) == (minstret&0xff)) {
|
||||||
|
val = static_cast<reg_t>(this->reg.instret);
|
||||||
|
} else if ((addr&0xff) == (minstreth&0xff)) {
|
||||||
|
if (sizeof(typename traits<BASE>::reg_t) != 4) return iss::Err;
|
||||||
|
val = static_cast<reg_t>(this->reg.instret >> 32);
|
||||||
|
}
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_instret(unsigned addr, reg_t val) {
|
||||||
|
if (sizeof(typename traits<BASE>::reg_t) != 4) {
|
||||||
|
if ((addr&0xff) == (minstreth&0xff))
|
||||||
|
return iss::Err;
|
||||||
|
this->reg.instret = static_cast<uint64_t>(val);
|
||||||
|
} else {
|
||||||
|
if ((addr&0xff) == (minstret&0xff)) {
|
||||||
|
this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val;
|
||||||
|
} else {
|
||||||
|
this->reg.instret = (static_cast<uint64_t>(val)<<32) + (this->reg.instret & 0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->reg.instret--;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned addr, reg_t &val) {
|
||||||
uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052;
|
uint64_t time_val = this->reg.icount / (100000000 / 32768 - 1); //-> ~3052;
|
||||||
if (addr == time) {
|
if (addr == time) {
|
||||||
val = static_cast<reg_t>(time_val);
|
val = static_cast<reg_t>(time_val);
|
||||||
} else if (addr == timeh) {
|
} else if (addr == timeh) {
|
||||||
@ -574,6 +724,11 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned ad
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_tvec(unsigned addr, reg_t &val) {
|
||||||
|
val = csr[mtvec] & ~2;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_status(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_status(unsigned addr, reg_t &val) {
|
||||||
val = state.mstatus & hart_state_type::get_mask();
|
val = state.mstatus & hart_state_type::get_mask();
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
@ -585,9 +740,13 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_status(unsigned
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cause(unsigned addr, reg_t val) {
|
||||||
|
csr[mcause] = val & ((1UL<<(traits<BASE>::XLEN-1))|0xf); //TODO: make exception code size configurable
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_ie(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_ie(unsigned addr, reg_t &val) {
|
||||||
val = csr[mie];
|
val = csr[mie];
|
||||||
val &= csr[mideleg];
|
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,7 +764,6 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_ie(unsigned add
|
|||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_ip(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_ip(unsigned addr, reg_t &val) {
|
||||||
val = csr[mip];
|
val = csr[mip];
|
||||||
val &= csr[mideleg];
|
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,9 +775,13 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_ip(unsigned add
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_epc(unsigned addr, reg_t val) {
|
||||||
|
csr[addr] = val & get_pc_mask();
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE>
|
template <typename BASE>
|
||||||
iss::status riscv_hart_m_p<BASE>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
|
iss::status riscv_hart_m_p<BASE>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
|
||||||
if(mem_read_cb) return mem_read_cb(paddr, length, data);
|
if(mem_read_cb) return mem_read_cb(paddr, length, data);
|
||||||
switch (paddr.val) {
|
switch (paddr.val) {
|
||||||
case 0x0200BFF8: { // CLINT base, mtime reg
|
case 0x0200BFF8: { // CLINT base, mtime reg
|
||||||
@ -635,9 +797,9 @@ iss::status riscv_hart_m_p<BASE>::read_mem(phys_addr_t paddr, unsigned length, u
|
|||||||
if (this->reg.icount > 30000) data[3] |= 0x80;
|
if (this->reg.icount > 30000) data[3] |= 0x80;
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
const auto &p = mem(paddr.val / mem.page_size);
|
for(auto offs=0U; offs<length; ++offs) {
|
||||||
auto offs = paddr.val & mem.page_addr_mask;
|
*(data + offs)=mem[(paddr.val+offs)%mem.size()];
|
||||||
std::copy(p.data() + offs, p.data() + offs + length, data);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
@ -645,7 +807,6 @@ iss::status riscv_hart_m_p<BASE>::read_mem(phys_addr_t paddr, unsigned length, u
|
|||||||
|
|
||||||
template <typename BASE>
|
template <typename BASE>
|
||||||
iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
|
iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
|
||||||
if(mem_write_cb) return mem_write_cb(paddr, length, data);
|
if(mem_write_cb) return mem_write_cb(paddr, length, data);
|
||||||
switch (paddr.val) {
|
switch (paddr.val) {
|
||||||
case 0x10013000: // UART0 base, TXFIFO reg
|
case 0x10013000: // UART0 base, TXFIFO reg
|
||||||
@ -677,9 +838,10 @@ iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length,
|
|||||||
std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask));
|
std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask));
|
||||||
// tohost handling in case of riscv-test
|
// tohost handling in case of riscv-test
|
||||||
if (paddr.access && iss::access_type::FUNC) {
|
if (paddr.access && iss::access_type::FUNC) {
|
||||||
auto tohost_upper = (traits<BASE>::XLEN == 32 && paddr.val == (tohost + 4));
|
auto tohost_upper = (traits<BASE>::XLEN == 32 && paddr.val == (tohost + 4)) ||
|
||||||
|
(traits<BASE>::XLEN == 64 && paddr.val == tohost);
|
||||||
auto tohost_lower =
|
auto tohost_lower =
|
||||||
(traits<BASE>::XLEN == 32 && paddr.val == tohost);
|
(traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
|
||||||
if (tohost_lower || tohost_upper) {
|
if (tohost_lower || tohost_upper) {
|
||||||
uint64_t hostvar = *reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask));
|
uint64_t hostvar = *reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask));
|
||||||
if (tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
|
if (tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
|
||||||
@ -710,7 +872,8 @@ iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length,
|
|||||||
}
|
}
|
||||||
} else if (tohost_lower)
|
} else if (tohost_lower)
|
||||||
to_host_wr_cnt++;
|
to_host_wr_cnt++;
|
||||||
} else if (traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) {
|
} else if ((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) ||
|
||||||
|
(traits<BASE>::XLEN == 64 && paddr.val == fromhost)) {
|
||||||
uint64_t fhostvar = *reinterpret_cast<uint64_t *>(p.data() + (fromhost & mem.page_addr_mask));
|
uint64_t fhostvar = *reinterpret_cast<uint64_t *>(p.data() + (fromhost & mem.page_addr_mask));
|
||||||
*reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
|
*reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
|
||||||
}
|
}
|
||||||
@ -726,7 +889,7 @@ template <typename BASE> inline void riscv_hart_m_p<BASE>::reset(uint64_t addres
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
|
template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
|
||||||
auto ideleg = csr[mideleg];
|
//auto ideleg = csr[mideleg];
|
||||||
// Multiple simultaneous interrupts and traps at the same privilege level are
|
// Multiple simultaneous interrupts and traps at the same privilege level are
|
||||||
// handled in the following decreasing priority order:
|
// handled in the following decreasing priority order:
|
||||||
// external interrupts, software interrupts, timer interrupts, then finally
|
// external interrupts, software interrupts, timer interrupts, then finally
|
||||||
@ -735,7 +898,7 @@ template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
|
|||||||
|
|
||||||
bool mie = state.mstatus.MIE;
|
bool mie = state.mstatus.MIE;
|
||||||
auto m_enabled = this->reg.PRIV < PRIV_M || (this->reg.PRIV == PRIV_M && mie);
|
auto m_enabled = this->reg.PRIV < PRIV_M || (this->reg.PRIV == PRIV_M && mie);
|
||||||
auto enabled_interrupts = m_enabled ? ena_irq & ~ideleg : 0;
|
auto enabled_interrupts = m_enabled ? ena_irq : 0;
|
||||||
|
|
||||||
if (enabled_interrupts != 0) {
|
if (enabled_interrupts != 0) {
|
||||||
int res = 0;
|
int res = 0;
|
||||||
@ -747,7 +910,7 @@ template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flags, uint64_t addr) {
|
template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) {
|
||||||
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
|
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
|
||||||
// calculate and write mcause val
|
// calculate and write mcause val
|
||||||
auto trap_id = bit_sub<0, 16>(flags);
|
auto trap_id = bit_sub<0, 16>(flags);
|
||||||
@ -756,11 +919,11 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flag
|
|||||||
// calculate effective privilege level
|
// calculate effective privilege level
|
||||||
if (trap_id == 0) { // exception
|
if (trap_id == 0) { // exception
|
||||||
// store ret addr in xepc register
|
// store ret addr in xepc register
|
||||||
csr[mepc] = static_cast<reg_t>(addr); // store actual address instruction of exception
|
csr[mepc] = static_cast<reg_t>(addr) & get_pc_mask(); // store actual address instruction of exception
|
||||||
csr[mtval] = fault_data;
|
csr[mtval] = cause==2?((instr & 0x3)==3?instr:instr&0xffff):fault_data;
|
||||||
fault_data = 0;
|
fault_data = 0;
|
||||||
} else {
|
} else {
|
||||||
csr[mepc] = this->reg.NEXT_PC; // store next address if interrupt
|
csr[mepc] = this->reg.NEXT_PC & get_pc_mask(); // store next address if interrupt
|
||||||
this->reg.pending_trap = 0;
|
this->reg.pending_trap = 0;
|
||||||
}
|
}
|
||||||
csr[mcause] = (trap_id << 31) + cause;
|
csr[mcause] = (trap_id << 31) + cause;
|
||||||
@ -779,13 +942,17 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flag
|
|||||||
auto ivec = csr[mtvec];
|
auto ivec = csr[mtvec];
|
||||||
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
||||||
// bits in mtvec
|
// bits in mtvec
|
||||||
this->reg.NEXT_PC = ivec & ~0x1UL;
|
this->reg.NEXT_PC = ivec & ~0x3UL;
|
||||||
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
|
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
|
||||||
// reset trap state
|
// reset trap state
|
||||||
this->reg.PRIV = PRIV_M;
|
this->reg.PRIV = PRIV_M;
|
||||||
this->reg.trap_state = 0;
|
this->reg.trap_state = 0;
|
||||||
std::array<char, 32> buffer;
|
std::array<char, 32> buffer;
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
sprintf(buffer.data(), "0x%016llx", addr);
|
||||||
|
#else
|
||||||
sprintf(buffer.data(), "0x%016lx", addr);
|
sprintf(buffer.data(), "0x%016lx", addr);
|
||||||
|
#endif
|
||||||
if((flags&0xffffffff) != 0xffffffff)
|
if((flags&0xffffffff) != 0xffffffff)
|
||||||
CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
|
CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
|
||||||
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
|
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
|
||||||
@ -795,9 +962,11 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flag
|
|||||||
|
|
||||||
template <typename BASE> uint64_t riscv_hart_m_p<BASE>::leave_trap(uint64_t flags) {
|
template <typename BASE> uint64_t riscv_hart_m_p<BASE>::leave_trap(uint64_t flags) {
|
||||||
state.mstatus.MIE = state.mstatus.MPIE;
|
state.mstatus.MIE = state.mstatus.MPIE;
|
||||||
|
state.mstatus.MPIE = 1;
|
||||||
// sets the pc to the value stored in the x epc register.
|
// sets the pc to the value stored in the x epc register.
|
||||||
this->reg.NEXT_PC = csr[mepc];
|
this->reg.NEXT_PC = csr[mepc] & get_pc_mask();
|
||||||
CLOG(INFO, disass) << "Executing xRET";
|
CLOG(INFO, disass) << "Executing xRET";
|
||||||
|
check_interrupt();
|
||||||
return this->reg.NEXT_PC;
|
return this->reg.NEXT_PC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,13 +43,14 @@
|
|||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
#define FMT_HEADER_ONLY
|
#define FMT_HEADER_ONLY
|
||||||
#endif
|
#endif
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <elfio/elfio.hpp>
|
#include <elfio/elfio.hpp>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
#include <util/bit_field.h>
|
#include <util/bit_field.h>
|
||||||
#include <util/ities.h>
|
#include <util/ities.h>
|
||||||
#include <util/sparse_array.h>
|
#include <util/sparse_array.h>
|
||||||
@ -90,12 +91,12 @@ protected:
|
|||||||
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
|
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
|
||||||
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
|
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
|
||||||
public:
|
public:
|
||||||
using super = BASE;
|
using core = BASE;
|
||||||
using this_class = riscv_hart_msu_vp<BASE>;
|
using this_class = riscv_hart_msu_vp<BASE>;
|
||||||
using virt_addr_t = typename super::virt_addr_t;
|
using virt_addr_t = typename core::virt_addr_t;
|
||||||
using phys_addr_t = typename super::phys_addr_t;
|
using phys_addr_t = typename core::phys_addr_t;
|
||||||
using reg_t = typename super::reg_t;
|
using reg_t = typename core::reg_t;
|
||||||
using addr_t = typename super::addr_t;
|
using addr_t = typename core::addr_t;
|
||||||
|
|
||||||
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
||||||
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
||||||
@ -272,8 +273,8 @@ public:
|
|||||||
};
|
};
|
||||||
using hart_state_type = hart_state<reg_t>;
|
using hart_state_type = hart_state<reg_t>;
|
||||||
|
|
||||||
const typename super::reg_t PGSIZE = 1 << PGSHIFT;
|
const typename core::reg_t PGSIZE = 1 << PGSHIFT;
|
||||||
const typename super::reg_t PGMASK = PGSIZE - 1;
|
const typename core::reg_t PGMASK = PGSIZE - 1;
|
||||||
|
|
||||||
constexpr reg_t get_irq_mask(size_t mode) {
|
constexpr reg_t get_irq_mask(size_t mode) {
|
||||||
std::array<const reg_t, 4> m = {{
|
std::array<const reg_t, 4> m = {{
|
||||||
@ -299,18 +300,30 @@ public:
|
|||||||
iss::status write(const address_type type, const access_type access, const uint32_t space,
|
iss::status write(const address_type type, const access_type access, const uint32_t space,
|
||||||
const uint64_t addr, const unsigned length, const uint8_t *const data) override;
|
const uint64_t addr, const unsigned length, const uint8_t *const data) override;
|
||||||
|
|
||||||
virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_msu_vp::enter_trap(flags, fault_data); }
|
virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_msu_vp::enter_trap(flags, fault_data, fault_data); }
|
||||||
virtual uint64_t enter_trap(uint64_t flags, uint64_t addr) override;
|
virtual uint64_t enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) override;
|
||||||
virtual uint64_t leave_trap(uint64_t flags) override;
|
virtual uint64_t leave_trap(uint64_t flags) override;
|
||||||
void wait_until(uint64_t flags) override;
|
void wait_until(uint64_t flags) override;
|
||||||
|
|
||||||
void disass_output(uint64_t pc, const std::string instr) override {
|
void disass_output(uint64_t pc, const std::string instr) override {
|
||||||
CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [p:{};s:0x{:x};c:{}]",
|
CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [p:{};s:0x{:x};c:{}]",
|
||||||
pc, instr, lvl[this->reg.PRIV], (reg_t)state.mstatus, this->reg.icount);
|
pc, instr, lvl[this->reg.PRIV], (reg_t)state.mstatus, this->reg.ccount);
|
||||||
};
|
};
|
||||||
|
|
||||||
iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; }
|
iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; }
|
||||||
|
|
||||||
|
void setMemReadCb(std::function<iss::status(phys_addr_t, unsigned, uint8_t* const)> const& memReadCb) {
|
||||||
|
mem_read_cb = memReadCb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMemWriteCb(std::function<iss::status(phys_addr_t, unsigned, const uint8_t* const)> const& memWriteCb) {
|
||||||
|
mem_write_cb = memWriteCb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_csr(unsigned addr, reg_t val){
|
||||||
|
csr[addr & csr.page_addr_mask] = val;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct riscv_instrumentation_if : public iss::instrumentation_if {
|
struct riscv_instrumentation_if : public iss::instrumentation_if {
|
||||||
|
|
||||||
@ -343,7 +356,10 @@ protected:
|
|||||||
virtual iss::status write_csr(unsigned addr, reg_t val);
|
virtual iss::status write_csr(unsigned addr, reg_t val);
|
||||||
|
|
||||||
hart_state_type state;
|
hart_state_type state;
|
||||||
uint64_t cycle_offset;
|
int64_t cycle_offset{0};
|
||||||
|
uint64_t mcycle_csr{0};
|
||||||
|
int64_t instret_offset{0};
|
||||||
|
uint64_t minstret_csr{0};
|
||||||
reg_t fault_data;
|
reg_t fault_data;
|
||||||
std::array<vm_info, 2> vm;
|
std::array<vm_info, 2> vm;
|
||||||
uint64_t tohost = tohost_dflt;
|
uint64_t tohost = tohost_dflt;
|
||||||
@ -364,19 +380,34 @@ protected:
|
|||||||
std::unordered_map<unsigned, wr_csr_f> csr_wr_cb;
|
std::unordered_map<unsigned, wr_csr_f> csr_wr_cb;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
iss::status read_reg(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_reg(unsigned addr, reg_t val);
|
||||||
|
iss::status read_null(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_null(unsigned addr, reg_t val){return iss::status::Ok;}
|
||||||
iss::status read_cycle(unsigned addr, reg_t &val);
|
iss::status read_cycle(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_cycle(unsigned addr, reg_t val);
|
||||||
|
iss::status read_instret(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_instret(unsigned addr, reg_t val);
|
||||||
|
iss::status read_mtvec(unsigned addr, reg_t &val);
|
||||||
iss::status read_time(unsigned addr, reg_t &val);
|
iss::status read_time(unsigned addr, reg_t &val);
|
||||||
iss::status read_status(unsigned addr, reg_t &val);
|
iss::status read_status(unsigned addr, reg_t &val);
|
||||||
iss::status write_status(unsigned addr, reg_t val);
|
iss::status write_status(unsigned addr, reg_t val);
|
||||||
|
iss::status write_cause(unsigned addr, reg_t val);
|
||||||
iss::status read_ie(unsigned addr, reg_t &val);
|
iss::status read_ie(unsigned addr, reg_t &val);
|
||||||
iss::status write_ie(unsigned addr, reg_t val);
|
iss::status write_ie(unsigned addr, reg_t val);
|
||||||
iss::status read_ip(unsigned addr, reg_t &val);
|
iss::status read_ip(unsigned addr, reg_t &val);
|
||||||
iss::status write_ip(unsigned addr, reg_t val);
|
iss::status write_ip(unsigned addr, reg_t val);
|
||||||
|
iss::status read_hartid(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_mepc(unsigned addr, reg_t val);
|
||||||
iss::status read_satp(unsigned addr, reg_t &val);
|
iss::status read_satp(unsigned addr, reg_t &val);
|
||||||
iss::status write_satp(unsigned addr, reg_t val);
|
iss::status write_satp(unsigned addr, reg_t val);
|
||||||
iss::status read_fcsr(unsigned addr, reg_t &val);
|
iss::status read_fcsr(unsigned addr, reg_t &val);
|
||||||
iss::status write_fcsr(unsigned addr, reg_t val);
|
iss::status write_fcsr(unsigned addr, reg_t val);
|
||||||
|
|
||||||
|
reg_t mhartid_reg{0x0};
|
||||||
|
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
|
||||||
|
std::function<iss::status(phys_addr_t, unsigned, const uint8_t *const)> mem_write_cb;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void check_interrupt();
|
void check_interrupt();
|
||||||
};
|
};
|
||||||
@ -384,29 +415,75 @@ protected:
|
|||||||
template <typename BASE>
|
template <typename BASE>
|
||||||
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
||||||
: state()
|
: state()
|
||||||
, cycle_offset(0)
|
|
||||||
, instr_if(*this) {
|
, instr_if(*this) {
|
||||||
csr[misa] = hart_state_type::get_misa();
|
// reset values
|
||||||
|
csr[misa] = traits<BASE>::MISA_VAL;
|
||||||
|
csr[mvendorid] = 0x669;
|
||||||
|
csr[marchid] = 0x80000003;
|
||||||
|
csr[mimpid] = 1;
|
||||||
|
|
||||||
uart_buf.str("");
|
uart_buf.str("");
|
||||||
// read-only registers
|
for (unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr){
|
||||||
csr_wr_cb[misa] = nullptr;
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr;
|
csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr;
|
}
|
||||||
// special handling
|
for (unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
|
}
|
||||||
|
for (unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
|
}
|
||||||
|
for (unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
}
|
||||||
|
for (unsigned addr = cycleh; addr <= hpmcounter31h; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
//csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
|
}
|
||||||
|
// common regs
|
||||||
|
const std::array<unsigned, 22> addrs{{
|
||||||
|
misa, mvendorid, marchid, mimpid,
|
||||||
|
mepc, mtvec, mscratch, mcause, mtval, mscratch,
|
||||||
|
sepc, stvec, sscratch, scause, stval, sscratch,
|
||||||
|
uepc, utvec, uscratch, ucause, utval, uscratch
|
||||||
|
}};
|
||||||
|
for(auto addr: addrs) {
|
||||||
|
csr_rd_cb[addr] = &this_class::read_reg;
|
||||||
|
csr_wr_cb[addr] = &this_class::write_reg;
|
||||||
|
}
|
||||||
|
// special handling & overrides
|
||||||
csr_rd_cb[time] = &this_class::read_time;
|
csr_rd_cb[time] = &this_class::read_time;
|
||||||
csr_wr_cb[time] = nullptr;
|
|
||||||
csr_rd_cb[timeh] = &this_class::read_time;
|
csr_rd_cb[timeh] = &this_class::read_time;
|
||||||
csr_wr_cb[timeh] = nullptr;
|
csr_rd_cb[cycle] = &this_class::read_cycle;
|
||||||
|
csr_rd_cb[cycleh] = &this_class::read_cycle;
|
||||||
|
csr_rd_cb[instret] = &this_class::read_instret;
|
||||||
|
csr_rd_cb[instreth] = &this_class::read_instret;
|
||||||
|
|
||||||
csr_rd_cb[mcycle] = &this_class::read_cycle;
|
csr_rd_cb[mcycle] = &this_class::read_cycle;
|
||||||
|
csr_wr_cb[mcycle] = &this_class::write_cycle;
|
||||||
csr_rd_cb[mcycleh] = &this_class::read_cycle;
|
csr_rd_cb[mcycleh] = &this_class::read_cycle;
|
||||||
csr_rd_cb[minstret] = &this_class::read_cycle;
|
csr_wr_cb[mcycleh] = &this_class::write_cycle;
|
||||||
csr_rd_cb[minstreth] = &this_class::read_cycle;
|
csr_rd_cb[minstret] = &this_class::read_instret;
|
||||||
|
csr_wr_cb[minstret] = &this_class::write_instret;
|
||||||
|
csr_rd_cb[minstreth] = &this_class::read_instret;
|
||||||
|
csr_wr_cb[minstreth] = &this_class::write_instret;
|
||||||
csr_rd_cb[mstatus] = &this_class::read_status;
|
csr_rd_cb[mstatus] = &this_class::read_status;
|
||||||
csr_wr_cb[mstatus] = &this_class::write_status;
|
csr_wr_cb[mstatus] = &this_class::write_status;
|
||||||
|
csr_wr_cb[mcause] = &this_class::write_cause;
|
||||||
csr_rd_cb[sstatus] = &this_class::read_status;
|
csr_rd_cb[sstatus] = &this_class::read_status;
|
||||||
csr_wr_cb[sstatus] = &this_class::write_status;
|
csr_wr_cb[sstatus] = &this_class::write_status;
|
||||||
|
csr_wr_cb[scause] = &this_class::write_cause;
|
||||||
csr_rd_cb[ustatus] = &this_class::read_status;
|
csr_rd_cb[ustatus] = &this_class::read_status;
|
||||||
csr_wr_cb[ustatus] = &this_class::write_status;
|
csr_wr_cb[ustatus] = &this_class::write_status;
|
||||||
|
csr_wr_cb[ucause] = &this_class::write_cause;
|
||||||
|
csr_rd_cb[mtvec] = &this_class::read_tvec;
|
||||||
|
csr_rd_cb[stvec] = &this_class::read_tvec;
|
||||||
|
csr_rd_cb[utvec] = &this_class::read_tvec;
|
||||||
|
csr_wr_cb[mepc] = &this_class::write_epc;
|
||||||
|
csr_wr_cb[sepc] = &this_class::write_epc;
|
||||||
|
csr_wr_cb[uepc] = &this_class::write_epc;
|
||||||
csr_rd_cb[mip] = &this_class::read_ip;
|
csr_rd_cb[mip] = &this_class::read_ip;
|
||||||
csr_wr_cb[mip] = &this_class::write_ip;
|
csr_wr_cb[mip] = &this_class::write_ip;
|
||||||
csr_rd_cb[sip] = &this_class::read_ip;
|
csr_rd_cb[sip] = &this_class::read_ip;
|
||||||
@ -419,6 +496,13 @@ riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
|||||||
csr_wr_cb[sie] = &this_class::write_ie;
|
csr_wr_cb[sie] = &this_class::write_ie;
|
||||||
csr_rd_cb[uie] = &this_class::read_ie;
|
csr_rd_cb[uie] = &this_class::read_ie;
|
||||||
csr_wr_cb[uie] = &this_class::write_ie;
|
csr_wr_cb[uie] = &this_class::write_ie;
|
||||||
|
csr_rd_cb[mhartid] = &this_class::read_hartid;
|
||||||
|
csr_rd_cb[mcounteren] = &this_class::read_null;
|
||||||
|
csr_wr_cb[mcounteren] = &this_class::write_null;
|
||||||
|
csr_wr_cb[misa] = &this_class::write_null;
|
||||||
|
csr_wr_cb[mvendorid] = &this_class::write_null;
|
||||||
|
csr_wr_cb[marchid] = &this_class::write_null;
|
||||||
|
csr_wr_cb[mimpid] = &this_class::write_null;
|
||||||
csr_rd_cb[satp] = &this_class::read_satp;
|
csr_rd_cb[satp] = &this_class::read_satp;
|
||||||
csr_wr_cb[satp] = &this_class::write_satp;
|
csr_wr_cb[satp] = &this_class::write_satp;
|
||||||
csr_rd_cb[fcsr] = &this_class::read_fcsr;
|
csr_rd_cb[fcsr] = &this_class::read_fcsr;
|
||||||
@ -447,6 +531,7 @@ template <typename BASE> std::pair<uint64_t, bool> riscv_hart_msu_vp<BASE>::load
|
|||||||
if (sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file");
|
if (sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file");
|
||||||
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
||||||
if (reader.get_machine() != EM_RISCV) throw std::runtime_error("wrong elf machine in file");
|
if (reader.get_machine() != EM_RISCV) throw std::runtime_error("wrong elf machine in file");
|
||||||
|
auto entry = reader.get_entry();
|
||||||
for (const auto pseg : reader.segments) {
|
for (const auto pseg : reader.segments) {
|
||||||
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
||||||
const auto seg_data = pseg->get_data();
|
const auto seg_data = pseg->get_data();
|
||||||
@ -459,14 +544,35 @@ template <typename BASE> std::pair<uint64_t, bool> riscv_hart_msu_vp<BASE>::load
|
|||||||
<< pseg->get_physical_address();
|
<< pseg->get_physical_address();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto sec : reader.sections) {
|
for(const auto sec : reader.sections) {
|
||||||
if (sec->get_name() == ".tohost") {
|
if(sec->get_name() == ".symtab") {
|
||||||
|
if ( SHT_SYMTAB == sec->get_type() ||
|
||||||
|
SHT_DYNSYM == sec->get_type() ) {
|
||||||
|
ELFIO::symbol_section_accessor symbols( reader, sec );
|
||||||
|
auto sym_no = symbols.get_symbols_num();
|
||||||
|
std::string name;
|
||||||
|
ELFIO::Elf64_Addr value = 0;
|
||||||
|
ELFIO::Elf_Xword size = 0;
|
||||||
|
unsigned char bind = 0;
|
||||||
|
unsigned char type = 0;
|
||||||
|
ELFIO::Elf_Half section = 0;
|
||||||
|
unsigned char other = 0;
|
||||||
|
for ( auto i = 0U; i < sym_no; ++i ) {
|
||||||
|
symbols.get_symbol( i, name, value, size, bind, type, section, other );
|
||||||
|
if(name=="tohost") {
|
||||||
|
tohost = value;
|
||||||
|
} else if(name=="fromhost") {
|
||||||
|
fromhost = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (sec->get_name() == ".tohost") {
|
||||||
tohost = sec->get_address();
|
tohost = sec->get_address();
|
||||||
fromhost = tohost + 0x40;
|
fromhost = tohost + 0x40;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(reader.get_entry(), true);
|
}
|
||||||
|
return std::make_pair(entry, true);
|
||||||
}
|
}
|
||||||
throw std::runtime_error("memory load file is not a valid elf file");
|
throw std::runtime_error("memory load file is not a valid elf file");
|
||||||
}
|
}
|
||||||
@ -509,10 +615,14 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
|
|||||||
auto res = type==iss::address_type::PHYSICAL?
|
auto res = type==iss::address_type::PHYSICAL?
|
||||||
read_mem( BASE::v2p(phys_addr_t{access, 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);
|
read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
|
||||||
if (unlikely(res != iss::Ok)) this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
|
if (unlikely(res != iss::Ok)){
|
||||||
|
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
|
||||||
|
fault_data=addr;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -549,6 +659,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -603,11 +714,14 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
|||||||
auto res = type==iss::address_type::PHYSICAL?
|
auto res = type==iss::address_type::PHYSICAL?
|
||||||
write_mem(phys_addr_t{access, space, addr}, 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);
|
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 = (1 << 31) | (5 << 16); // issue trap 7 (Store/AMO access fault)
|
this->reg.trap_state = (1 << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)
|
||||||
|
fault_data=addr;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,6 +786,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -679,35 +794,43 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
|||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_csr(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_csr(unsigned addr, reg_t &val) {
|
||||||
if (addr >= csr.size()) return iss::Err;
|
if (addr >= csr.size()) return iss::Err;
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
auto req_priv_lvl = (addr >> 8) & 0x3;
|
||||||
if (this->reg.PRIV < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
if (this->reg.PRIV < req_priv_lvl) // not having required privileges
|
||||||
|
throw illegal_instruction_fault(this->fault_data);
|
||||||
auto it = csr_rd_cb.find(addr);
|
auto it = csr_rd_cb.find(addr);
|
||||||
if (it == csr_rd_cb.end()) {
|
if (it == csr_rd_cb.end() || !it->second) // non existent register
|
||||||
val = csr[addr & csr.page_addr_mask];
|
throw illegal_instruction_fault(this->fault_data);
|
||||||
return iss::Ok;
|
return (this->*(it->second))(addr, val);
|
||||||
}
|
|
||||||
rd_csr_f f = it->second;
|
|
||||||
if (f == nullptr) throw illegal_instruction_fault(this->fault_data);
|
|
||||||
return (this->*f)(addr, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_csr(unsigned addr, reg_t val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_csr(unsigned addr, reg_t val) {
|
||||||
if (addr >= csr.size()) return iss::Err;
|
if (addr >= csr.size()) return iss::Err;
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
auto req_priv_lvl = (addr >> 8) & 0x3;
|
||||||
if (this->reg.PRIV < req_priv_lvl)
|
if (this->reg.PRIV < req_priv_lvl) // not having required privileges
|
||||||
throw illegal_instruction_fault(this->fault_data);
|
throw illegal_instruction_fault(this->fault_data);
|
||||||
if((addr&0xc00)==0xc00)
|
if((addr&0xc00)==0xc00) // writing to read-only region
|
||||||
throw illegal_instruction_fault(this->fault_data);
|
throw illegal_instruction_fault(this->fault_data);
|
||||||
auto it = csr_wr_cb.find(addr);
|
auto it = csr_wr_cb.find(addr);
|
||||||
if (it == csr_wr_cb.end()) {
|
if (it == csr_wr_cb.end() || !it->second) // non existent register
|
||||||
csr[addr & csr.page_addr_mask] = val;
|
throw illegal_instruction_fault(this->fault_data);
|
||||||
return iss::Ok;
|
return (this->*(it->second))(addr, val);
|
||||||
}
|
|
||||||
wr_csr_f f = it->second;
|
|
||||||
if (f == nullptr) throw illegal_instruction_fault(this->fault_data);
|
|
||||||
return (this->*f)(addr, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_cycle(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_reg(unsigned addr, reg_t &val) {
|
||||||
|
val = csr[addr];
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_null(unsigned addr, reg_t &val) {
|
||||||
|
val = 0;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_reg(unsigned addr, reg_t val) {
|
||||||
|
csr[addr] = val;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_cycle(unsigned addr, reg_t &val) {
|
||||||
auto cycle_val = this->reg.icount + cycle_offset;
|
auto cycle_val = this->reg.icount + cycle_offset;
|
||||||
if (addr == mcycle) {
|
if (addr == mcycle) {
|
||||||
val = static_cast<reg_t>(cycle_val);
|
val = static_cast<reg_t>(cycle_val);
|
||||||
@ -718,8 +841,50 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_cycle(unsigne
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_time(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cycle(unsigned addr, reg_t val) {
|
||||||
uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052;
|
if (sizeof(typename traits<BASE>::reg_t) != 4) {
|
||||||
|
if (addr == mcycleh)
|
||||||
|
return iss::Err;
|
||||||
|
mcycle_csr = static_cast<uint64_t>(val);
|
||||||
|
} else {
|
||||||
|
if (addr == mcycle) {
|
||||||
|
mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val;
|
||||||
|
} else {
|
||||||
|
mcycle_csr = (static_cast<uint64_t>(val)<<32) + (mcycle_csr & 0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cycle_offset = mcycle_csr-this->reg.icount; // TODO: relying on wrap-around
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_instret(unsigned addr, reg_t &val) {
|
||||||
|
if ((addr&0xff) == (minstret&0xff)) {
|
||||||
|
val = static_cast<reg_t>(this->reg.instret);
|
||||||
|
} else if ((addr&0xff) == (minstreth&0xff)) {
|
||||||
|
if (sizeof(typename traits<BASE>::reg_t) != 4) return iss::Err;
|
||||||
|
val = static_cast<reg_t>(this->reg.instret >> 32);
|
||||||
|
}
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_instret(unsigned addr, reg_t val) {
|
||||||
|
if (sizeof(typename traits<BASE>::reg_t) != 4) {
|
||||||
|
if ((addr&0xff) == (minstreth&0xff))
|
||||||
|
return iss::Err;
|
||||||
|
this->reg.instret = static_cast<uint64_t>(val);
|
||||||
|
} else {
|
||||||
|
if ((addr&0xff) == (minstret&0xff)) {
|
||||||
|
this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val;
|
||||||
|
} else {
|
||||||
|
this->reg.instret = (static_cast<uint64_t>(val)<<32) + (this->reg.instret & 0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->reg.instret--;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned addr, reg_t &val) {
|
||||||
|
uint64_t time_val = this->reg.icount / (100000000 / 32768 - 1); //-> ~3052;
|
||||||
if (addr == time) {
|
if (addr == time) {
|
||||||
val = static_cast<reg_t>(time_val);
|
val = static_cast<reg_t>(time_val);
|
||||||
} else if (addr == timeh) {
|
} else if (addr == timeh) {
|
||||||
@ -729,6 +894,11 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_time(unsigned
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_tvec(unsigned addr, reg_t &val) {
|
||||||
|
val = csr[addr] & ~2;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t &val) {
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
auto req_priv_lvl = (addr >> 8) & 0x3;
|
||||||
val = state.mstatus & hart_state_type::get_mask(req_priv_lvl);
|
val = state.mstatus & hart_state_type::get_mask(req_priv_lvl);
|
||||||
@ -743,6 +913,11 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_status(unsig
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_cause(unsigned addr, reg_t val) {
|
||||||
|
csr[addr] = val & ((1UL<<(traits<BASE>::XLEN-1))|0xf); //TODO: make exception code size configurable
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ie(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ie(unsigned addr, reg_t &val) {
|
||||||
val = csr[mie];
|
val = csr[mie];
|
||||||
if (addr < mie) val &= csr[mideleg];
|
if (addr < mie) val &= csr[mideleg];
|
||||||
@ -750,6 +925,11 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ie(unsigned a
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_hartid(unsigned addr, reg_t &val) {
|
||||||
|
val = mhartid_reg;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ie(unsigned addr, reg_t val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ie(unsigned addr, reg_t val) {
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
auto req_priv_lvl = (addr >> 8) & 0x3;
|
||||||
auto mask = get_irq_mask(req_priv_lvl);
|
auto mask = get_irq_mask(req_priv_lvl);
|
||||||
@ -774,6 +954,11 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ip(unsigned
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_epc(unsigned addr, reg_t val) {
|
||||||
|
csr[addr] = val & get_pc_mask();
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned addr, reg_t &val) {
|
||||||
reg_t tvm = state.mstatus.TVM;
|
reg_t tvm = state.mstatus.TVM;
|
||||||
if (this->reg.PRIV == PRIV_S & tvm != 0) {
|
if (this->reg.PRIV == PRIV_S & tvm != 0) {
|
||||||
@ -832,7 +1017,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_fcsr(unsigne
|
|||||||
|
|
||||||
template <typename BASE>
|
template <typename BASE>
|
||||||
iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
|
iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
if(mem_read_cb) return mem_read_cb(paddr, length, data);
|
||||||
switch (paddr.val) {
|
switch (paddr.val) {
|
||||||
case 0x0200BFF8: { // CLINT base, mtime reg
|
case 0x0200BFF8: { // CLINT base, mtime reg
|
||||||
if (sizeof(reg_t) < length) return iss::Err;
|
if (sizeof(reg_t) < length) return iss::Err;
|
||||||
@ -847,9 +1032,9 @@ iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length
|
|||||||
if (this->reg.icount > 30000) data[3] |= 0x80;
|
if (this->reg.icount > 30000) data[3] |= 0x80;
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
const auto &p = mem(paddr.val / mem.page_size);
|
for(auto offs=0U; offs<length; ++offs) {
|
||||||
auto offs = paddr.val & mem.page_addr_mask;
|
*(data + offs)=mem[(paddr.val+offs)%mem.size()];
|
||||||
std::copy(p.data() + offs, p.data() + offs + length, data);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
@ -857,7 +1042,7 @@ iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length
|
|||||||
|
|
||||||
template <typename BASE>
|
template <typename BASE>
|
||||||
iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
|
iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
if(mem_write_cb) return mem_write_cb(paddr, length, data);
|
||||||
switch (paddr.val) {
|
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
|
||||||
@ -1070,7 +1255,7 @@ typename riscv_hart_msu_vp<BASE>::phys_addr_t riscv_hart_msu_vp<BASE>::virt2phys
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t flags, uint64_t addr) {
|
template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) {
|
||||||
auto cur_priv = this->reg.PRIV;
|
auto cur_priv = this->reg.PRIV;
|
||||||
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
|
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
|
||||||
// calculate and write mcause val
|
// calculate and write mcause val
|
||||||
@ -1091,7 +1276,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
|||||||
* access, or page-fault exception occurs, mtval is written with the
|
* access, or page-fault exception occurs, mtval is written with the
|
||||||
* faulting effective address.
|
* faulting effective address.
|
||||||
*/
|
*/
|
||||||
csr[utval | (new_priv << 8)] = fault_data;
|
csr[utval | (new_priv << 8)] = cause==2?((instr & 0x3)==3?instr:instr&0xffff):fault_data;
|
||||||
fault_data = 0;
|
fault_data = 0;
|
||||||
} else {
|
} else {
|
||||||
if (cur_priv != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)
|
if (cur_priv != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)
|
||||||
@ -1131,11 +1316,8 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
|||||||
auto ivec = csr[utvec | (new_priv << 8)];
|
auto ivec = csr[utvec | (new_priv << 8)];
|
||||||
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
||||||
// bits in mtvec
|
// bits in mtvec
|
||||||
this->reg.NEXT_PC = ivec & ~0x1UL;
|
this->reg.NEXT_PC = ivec & ~0x3UL;
|
||||||
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
|
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
|
||||||
// reset trap state
|
|
||||||
this->reg.PRIV = new_priv;
|
|
||||||
this->reg.trap_state = 0;
|
|
||||||
std::array<char, 32> buffer;
|
std::array<char, 32> buffer;
|
||||||
sprintf(buffer.data(), "0x%016lx", addr);
|
sprintf(buffer.data(), "0x%016lx", addr);
|
||||||
if((flags&0xffffffff) != 0xffffffff)
|
if((flags&0xffffffff) != 0xffffffff)
|
||||||
@ -1143,6 +1325,9 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
|||||||
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
|
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
|
||||||
<< " at address " << buffer.data() << " occurred, changing privilege level from "
|
<< " at address " << buffer.data() << " occurred, changing privilege level from "
|
||||||
<< lvl[cur_priv] << " to " << lvl[new_priv];
|
<< lvl[cur_priv] << " to " << lvl[new_priv];
|
||||||
|
// reset trap state
|
||||||
|
this->reg.PRIV = new_priv;
|
||||||
|
this->reg.trap_state = 0;
|
||||||
update_vm_info();
|
update_vm_info();
|
||||||
return this->reg.NEXT_PC;
|
return this->reg.NEXT_PC;
|
||||||
}
|
}
|
||||||
@ -1166,15 +1351,18 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t f
|
|||||||
this->reg.PRIV = state.mstatus.MPP;
|
this->reg.PRIV = state.mstatus.MPP;
|
||||||
state.mstatus.MPP = 0; // clear mpp to U mode
|
state.mstatus.MPP = 0; // clear mpp to U mode
|
||||||
state.mstatus.MIE = state.mstatus.MPIE;
|
state.mstatus.MIE = state.mstatus.MPIE;
|
||||||
|
state.mstatus.MPIE = 1;
|
||||||
break;
|
break;
|
||||||
case PRIV_S:
|
case PRIV_S:
|
||||||
this->reg.PRIV = state.mstatus.SPP;
|
this->reg.PRIV = state.mstatus.SPP;
|
||||||
state.mstatus.SPP = 0; // clear spp to U mode
|
state.mstatus.SPP = 0; // clear spp to U mode
|
||||||
state.mstatus.SIE = state.mstatus.SPIE;
|
state.mstatus.SIE = state.mstatus.SPIE;
|
||||||
|
state.mstatus.SPIE = 1;
|
||||||
break;
|
break;
|
||||||
case PRIV_U:
|
case PRIV_U:
|
||||||
this->reg.PRIV = 0;
|
this->reg.PRIV = 0;
|
||||||
state.mstatus.UIE = state.mstatus.UPIE;
|
state.mstatus.UIE = state.mstatus.UPIE;
|
||||||
|
state.mstatus.UPIE = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// sets the pc to the value stored in the x epc register.
|
// sets the pc to the value stored in the x epc register.
|
||||||
@ -1182,6 +1370,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t f
|
|||||||
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to "
|
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to "
|
||||||
<< lvl[this->reg.PRIV];
|
<< lvl[this->reg.PRIV];
|
||||||
update_vm_info();
|
update_vm_info();
|
||||||
|
check_interrupt();
|
||||||
return this->reg.NEXT_PC;
|
return this->reg.NEXT_PC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
namespace arch {
|
||||||
|
|
||||||
enum features_e{FEAT_NONE, FEAT_PMP, FEAT_EXT_N};
|
enum features_e{FEAT_NONE, FEAT_PMP, FEAT_EXT_N, FEAT_CLIC};
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT=FEAT_NONE> class riscv_hart_mu_p : public BASE {
|
template <typename BASE, features_e FEAT=FEAT_NONE> class riscv_hart_mu_p : public BASE {
|
||||||
protected:
|
protected:
|
||||||
@ -93,11 +93,11 @@ protected:
|
|||||||
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
|
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
|
||||||
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
|
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
|
||||||
public:
|
public:
|
||||||
using super = BASE;
|
using core = BASE;
|
||||||
using this_class = riscv_hart_mu_p<BASE, FEAT>;
|
using this_class = riscv_hart_mu_p<BASE, FEAT>;
|
||||||
using phys_addr_t = typename super::phys_addr_t;
|
using phys_addr_t = typename core::phys_addr_t;
|
||||||
using reg_t = typename super::reg_t;
|
using reg_t = typename core::reg_t;
|
||||||
using addr_t = typename super::addr_t;
|
using addr_t = typename core::addr_t;
|
||||||
|
|
||||||
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
||||||
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
||||||
@ -154,31 +154,49 @@ public:
|
|||||||
mstatus = new_val;
|
mstatus = new_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
T satp;
|
|
||||||
|
|
||||||
static constexpr uint32_t get_mask(unsigned priv_lvl) {
|
static constexpr uint32_t get_mask(unsigned priv_lvl) {
|
||||||
#if __cplusplus < 201402L
|
#if __cplusplus < 201402L
|
||||||
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
|
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
|
||||||
#else
|
#else
|
||||||
switch (priv_lvl) {
|
switch (priv_lvl) {
|
||||||
case PRIV_U: return 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
case PRIV_U: return 0x00000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
||||||
default: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
|
default:
|
||||||
|
// +-SD
|
||||||
|
// | +-TSR
|
||||||
|
// | |+-TW
|
||||||
|
// | ||+-TVM
|
||||||
|
// | |||+-MXR
|
||||||
|
// | ||||+-SUM
|
||||||
|
// | |||||+-MPRV
|
||||||
|
// | |||||| +-XS
|
||||||
|
// | |||||| | +-FS
|
||||||
|
// | |||||| | | +-MPP
|
||||||
|
// | |||||| | | | +-SPP
|
||||||
|
// | |||||| | | | |+-MPIE
|
||||||
|
// | |||||| | | | || +-UPIE
|
||||||
|
// | ||||||/|/|/| || |+-MIE
|
||||||
|
// | ||||||/|/|/| || || +-UIE
|
||||||
|
return 0b00000000000000000001100010011001;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
using hart_state_type = hart_state<reg_t>;
|
using hart_state_type = hart_state<reg_t>;
|
||||||
|
|
||||||
constexpr reg_t get_irq_mask(size_t mode) {
|
constexpr reg_t get_irq_wrmask(size_t mode) {
|
||||||
std::array<const reg_t, 4> m = {{
|
std::array<const reg_t, 4> m = {{
|
||||||
0b000100010001, // U mode
|
0b000100010001, // U mode
|
||||||
0b001100110011, // S mode
|
0b001100110011, // S mode
|
||||||
0,
|
0,
|
||||||
0b101110111011 // M mode
|
0b100110011001 // M mode
|
||||||
}};
|
}};
|
||||||
return m[mode];
|
return m[mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr reg_t get_pc_mask() {
|
||||||
|
return traits<BASE>::MISA_VAL&0b0100?~1:~3;
|
||||||
|
}
|
||||||
|
|
||||||
riscv_hart_mu_p();
|
riscv_hart_mu_p();
|
||||||
virtual ~riscv_hart_mu_p() = default;
|
virtual ~riscv_hart_mu_p() = default;
|
||||||
|
|
||||||
@ -191,8 +209,8 @@ public:
|
|||||||
iss::status write(const address_type type, const access_type access, const uint32_t space,
|
iss::status write(const address_type type, const access_type access, const uint32_t space,
|
||||||
const uint64_t addr, const unsigned length, const uint8_t *const data) override;
|
const uint64_t addr, const unsigned length, const uint8_t *const data) override;
|
||||||
|
|
||||||
virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_mu_p::enter_trap(flags, fault_data); }
|
virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_mu_p::enter_trap(flags, fault_data, fault_data); }
|
||||||
virtual uint64_t enter_trap(uint64_t flags, uint64_t addr) override;
|
virtual uint64_t enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) override;
|
||||||
virtual uint64_t leave_trap(uint64_t flags) override;
|
virtual uint64_t leave_trap(uint64_t flags) override;
|
||||||
|
|
||||||
const reg_t& get_mhartid() const { return mhartid_reg; }
|
const reg_t& get_mhartid() const { return mhartid_reg; }
|
||||||
@ -245,11 +263,17 @@ protected:
|
|||||||
virtual iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data);
|
virtual iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data);
|
||||||
virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t *const data);
|
virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t *const data);
|
||||||
|
|
||||||
|
iss::status read_clic(uint64_t addr, unsigned length, uint8_t *const data);
|
||||||
|
iss::status write_clic(uint64_t addr, unsigned length, const uint8_t *const data);
|
||||||
|
|
||||||
virtual iss::status read_csr(unsigned addr, reg_t &val);
|
virtual iss::status read_csr(unsigned addr, reg_t &val);
|
||||||
virtual iss::status write_csr(unsigned addr, reg_t val);
|
virtual iss::status write_csr(unsigned addr, reg_t val);
|
||||||
|
|
||||||
hart_state_type state;
|
hart_state_type state;
|
||||||
uint64_t cycle_offset;
|
int64_t cycle_offset{0};
|
||||||
|
uint64_t mcycle_csr{0};
|
||||||
|
int64_t instret_offset{0};
|
||||||
|
uint64_t minstret_csr{0};
|
||||||
reg_t fault_data;
|
reg_t fault_data;
|
||||||
uint64_t tohost = tohost_dflt;
|
uint64_t tohost = tohost_dflt;
|
||||||
uint64_t fromhost = fromhost_dflt;
|
uint64_t fromhost = fromhost_dflt;
|
||||||
@ -266,19 +290,44 @@ protected:
|
|||||||
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
|
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
|
||||||
std::unordered_map<unsigned, rd_csr_f> csr_rd_cb;
|
std::unordered_map<unsigned, rd_csr_f> csr_rd_cb;
|
||||||
std::unordered_map<unsigned, wr_csr_f> csr_wr_cb;
|
std::unordered_map<unsigned, wr_csr_f> csr_wr_cb;
|
||||||
|
uint8_t clic_cfg_reg{0};
|
||||||
|
uint32_t clic_info_reg{0};
|
||||||
|
std::array<uint32_t, 32> clic_inttrig_reg;
|
||||||
|
union clic_int_reg_t {
|
||||||
|
struct{
|
||||||
|
uint8_t ip;
|
||||||
|
uint8_t ie;
|
||||||
|
uint8_t attr;
|
||||||
|
uint8_t ctl;
|
||||||
|
};
|
||||||
|
uint32_t raw;
|
||||||
|
};
|
||||||
|
std::vector<clic_int_reg_t> clic_int_reg;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
iss::status read_reg(unsigned addr, reg_t &val);
|
iss::status read_csr_reg(unsigned addr, reg_t &val);
|
||||||
iss::status write_reg(unsigned addr, reg_t val);
|
iss::status write_csr_reg(unsigned addr, reg_t val);
|
||||||
|
iss::status read_null(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_null(unsigned addr, reg_t val){return iss::status::Ok;}
|
||||||
iss::status read_cycle(unsigned addr, reg_t &val);
|
iss::status read_cycle(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_cycle(unsigned addr, reg_t val);
|
||||||
|
iss::status read_instret(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_instret(unsigned addr, reg_t val);
|
||||||
|
iss::status read_tvec(unsigned addr, reg_t &val);
|
||||||
iss::status read_time(unsigned addr, reg_t &val);
|
iss::status read_time(unsigned addr, reg_t &val);
|
||||||
iss::status read_status(unsigned addr, reg_t &val);
|
iss::status read_status(unsigned addr, reg_t &val);
|
||||||
iss::status write_status(unsigned addr, reg_t val);
|
iss::status write_status(unsigned addr, reg_t val);
|
||||||
|
iss::status write_cause(unsigned addr, reg_t val);
|
||||||
iss::status read_ie(unsigned addr, reg_t &val);
|
iss::status read_ie(unsigned addr, reg_t &val);
|
||||||
iss::status write_ie(unsigned addr, reg_t val);
|
iss::status write_ie(unsigned addr, reg_t val);
|
||||||
iss::status read_ip(unsigned addr, reg_t &val);
|
iss::status read_ip(unsigned addr, reg_t &val);
|
||||||
iss::status write_ip(unsigned addr, reg_t val);
|
iss::status write_ip(unsigned addr, reg_t val);
|
||||||
|
iss::status write_ideleg(unsigned addr, reg_t val);
|
||||||
|
iss::status write_edeleg(unsigned addr, reg_t val);
|
||||||
iss::status read_hartid(unsigned addr, reg_t &val);
|
iss::status read_hartid(unsigned addr, reg_t &val);
|
||||||
|
iss::status write_epc(unsigned addr, reg_t val);
|
||||||
|
iss::status write_intstatus(unsigned addr, reg_t val);
|
||||||
|
iss::status write_intthresh(unsigned addr, reg_t val);
|
||||||
|
|
||||||
reg_t mhartid_reg{0x0};
|
reg_t mhartid_reg{0x0};
|
||||||
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
|
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
|
||||||
@ -286,64 +335,134 @@ private:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void check_interrupt();
|
void check_interrupt();
|
||||||
|
bool pmp_check(const access_type type, const uint64_t addr, const unsigned len);
|
||||||
|
uint64_t clic_base_addr{0};
|
||||||
|
unsigned clic_num_irq{0};
|
||||||
|
unsigned clic_num_trigger{0};
|
||||||
|
unsigned mcause_max_irq{16};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT>
|
template <typename BASE, features_e FEAT>
|
||||||
riscv_hart_mu_p<BASE, FEAT>::riscv_hart_mu_p()
|
riscv_hart_mu_p<BASE, FEAT>::riscv_hart_mu_p()
|
||||||
: state()
|
: state()
|
||||||
, cycle_offset(0)
|
|
||||||
, instr_if(*this) {
|
, instr_if(*this) {
|
||||||
|
// reset values
|
||||||
csr[misa] = traits<BASE>::MISA_VAL;
|
csr[misa] = traits<BASE>::MISA_VAL;
|
||||||
|
csr[mvendorid] = 0x669;
|
||||||
|
csr[marchid] = 0x80000004;
|
||||||
|
csr[mimpid] = 1;
|
||||||
|
csr[mclicbase] = 0xc0000000; // TODO: should be taken from YAML file
|
||||||
|
|
||||||
uart_buf.str("");
|
uart_buf.str("");
|
||||||
for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr;
|
for (unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr){
|
||||||
for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr;
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
// special handling
|
csr_wr_cb[addr] = &this_class::write_csr_reg;
|
||||||
|
}
|
||||||
|
for (unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
csr_wr_cb[addr] = &this_class::write_csr_reg;
|
||||||
|
}
|
||||||
|
for (unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
csr_wr_cb[addr] = &this_class::write_csr_reg;
|
||||||
|
}
|
||||||
|
for (unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
}
|
||||||
|
for (unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr){
|
||||||
|
csr_rd_cb[addr] = &this_class::read_null;
|
||||||
|
//csr_wr_cb[addr] = &this_class::write_csr_reg;
|
||||||
|
}
|
||||||
|
// common regs
|
||||||
|
const std::array<unsigned, 14> addrs{{
|
||||||
|
misa, mvendorid, marchid, mimpid,
|
||||||
|
mepc, mtvec, mscratch, mcause, mtval,
|
||||||
|
uepc, utvec, uscratch, ucause, utval,
|
||||||
|
}};
|
||||||
|
for(auto addr: addrs) {
|
||||||
|
csr_rd_cb[addr] = &this_class::read_csr_reg;
|
||||||
|
csr_wr_cb[addr] = &this_class::write_csr_reg;
|
||||||
|
}
|
||||||
|
// special handling & overrides
|
||||||
csr_rd_cb[time] = &this_class::read_time;
|
csr_rd_cb[time] = &this_class::read_time;
|
||||||
csr_wr_cb[time] = nullptr;
|
|
||||||
csr_rd_cb[timeh] = &this_class::read_time;
|
csr_rd_cb[timeh] = &this_class::read_time;
|
||||||
csr_wr_cb[timeh] = nullptr;
|
csr_rd_cb[cycle] = &this_class::read_cycle;
|
||||||
|
csr_rd_cb[cycleh] = &this_class::read_cycle;
|
||||||
|
csr_rd_cb[instret] = &this_class::read_instret;
|
||||||
|
csr_rd_cb[instreth] = &this_class::read_instret;
|
||||||
|
|
||||||
csr_rd_cb[mcycle] = &this_class::read_cycle;
|
csr_rd_cb[mcycle] = &this_class::read_cycle;
|
||||||
|
csr_wr_cb[mcycle] = &this_class::write_cycle;
|
||||||
csr_rd_cb[mcycleh] = &this_class::read_cycle;
|
csr_rd_cb[mcycleh] = &this_class::read_cycle;
|
||||||
csr_rd_cb[minstret] = &this_class::read_cycle;
|
csr_wr_cb[mcycleh] = &this_class::write_cycle;
|
||||||
csr_rd_cb[minstreth] = &this_class::read_cycle;
|
csr_rd_cb[minstret] = &this_class::read_instret;
|
||||||
|
csr_wr_cb[minstret] = &this_class::write_instret;
|
||||||
|
csr_rd_cb[minstreth] = &this_class::read_instret;
|
||||||
|
csr_wr_cb[minstreth] = &this_class::write_instret;
|
||||||
csr_rd_cb[mstatus] = &this_class::read_status;
|
csr_rd_cb[mstatus] = &this_class::read_status;
|
||||||
csr_wr_cb[mstatus] = &this_class::write_status;
|
csr_wr_cb[mstatus] = &this_class::write_status;
|
||||||
csr_rd_cb[ustatus] = &this_class::read_status;
|
csr_wr_cb[mcause] = &this_class::write_cause;
|
||||||
csr_wr_cb[ustatus] = &this_class::write_status;
|
csr_rd_cb[mtvec] = &this_class::read_tvec;
|
||||||
|
csr_wr_cb[mepc] = &this_class::write_epc;
|
||||||
csr_rd_cb[mip] = &this_class::read_ip;
|
csr_rd_cb[mip] = &this_class::read_ip;
|
||||||
csr_wr_cb[mip] = &this_class::write_ip;
|
csr_wr_cb[mip] = &this_class::write_ip;
|
||||||
csr_rd_cb[uip] = &this_class::read_ip;
|
|
||||||
csr_wr_cb[uip] = &this_class::write_ip;
|
|
||||||
csr_rd_cb[mie] = &this_class::read_ie;
|
csr_rd_cb[mie] = &this_class::read_ie;
|
||||||
csr_wr_cb[mie] = &this_class::write_ie;
|
csr_wr_cb[mie] = &this_class::write_ie;
|
||||||
csr_rd_cb[uie] = &this_class::read_ie;
|
|
||||||
csr_wr_cb[uie] = &this_class::write_ie;
|
|
||||||
csr_rd_cb[mhartid] = &this_class::read_hartid;
|
csr_rd_cb[mhartid] = &this_class::read_hartid;
|
||||||
// common regs
|
csr_rd_cb[mcounteren] = &this_class::read_null;
|
||||||
const std::array<unsigned, 6> addrs{{mepc, mtvec, mscratch, mcause, mtval, mscratch}};
|
csr_wr_cb[mcounteren] = &this_class::write_null;
|
||||||
for(auto addr: addrs) {
|
csr_wr_cb[misa] = &this_class::write_null;
|
||||||
csr_rd_cb[addr] = &this_class::read_reg;
|
csr_wr_cb[mvendorid] = &this_class::write_null;
|
||||||
csr_wr_cb[addr] = &this_class::write_reg;
|
csr_wr_cb[marchid] = &this_class::write_null;
|
||||||
}
|
csr_wr_cb[mimpid] = &this_class::write_null;
|
||||||
// read-only registers
|
|
||||||
csr_rd_cb[misa] = &this_class::read_reg;
|
|
||||||
csr_wr_cb[misa] = nullptr;
|
|
||||||
|
|
||||||
if(FEAT & FEAT_PMP){
|
if(FEAT & FEAT_PMP){
|
||||||
for(size_t i=pmpaddr0; i<=pmpaddr15; ++i){
|
for(size_t i=pmpaddr0; i<=pmpaddr15; ++i){
|
||||||
csr_rd_cb[i] = &this_class::read_reg;
|
csr_rd_cb[i] = &this_class::read_csr_reg;
|
||||||
csr_wr_cb[i] = &this_class::write_reg;
|
csr_wr_cb[i] = &this_class::write_csr_reg;
|
||||||
}
|
}
|
||||||
for(size_t i=pmpcfg0; i<=pmpcfg3; ++i){
|
for(size_t i=pmpcfg0; i<=pmpcfg3; ++i){
|
||||||
csr_rd_cb[i] = &this_class::read_reg;
|
csr_rd_cb[i] = &this_class::read_csr_reg;
|
||||||
csr_wr_cb[i] = &this_class::write_reg;
|
csr_wr_cb[i] = &this_class::write_csr_reg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(FEAT & FEAT_EXT_N){
|
if(FEAT & FEAT_EXT_N){
|
||||||
csr_rd_cb[mideleg] = &this_class::read_reg;
|
csr_rd_cb[mideleg] = &this_class::read_csr_reg;
|
||||||
csr_wr_cb[mideleg] = &this_class::write_reg;
|
csr_wr_cb[mideleg] = &this_class::write_ideleg;
|
||||||
csr_rd_cb[medeleg] = &this_class::read_reg;
|
csr_rd_cb[medeleg] = &this_class::read_csr_reg;
|
||||||
csr_wr_cb[medeleg] = &this_class::write_reg;
|
csr_wr_cb[medeleg] = &this_class::write_edeleg;
|
||||||
|
csr_rd_cb[uie] = &this_class::read_ie;
|
||||||
|
csr_wr_cb[uie] = &this_class::write_ie;
|
||||||
|
csr_rd_cb[uip] = &this_class::read_ip;
|
||||||
|
csr_wr_cb[uip] = &this_class::write_ip;
|
||||||
|
csr_wr_cb[uepc] = &this_class::write_epc;
|
||||||
|
csr_rd_cb[ustatus] = &this_class::read_status;
|
||||||
|
csr_wr_cb[ustatus] = &this_class::write_status;
|
||||||
|
csr_wr_cb[ucause] = &this_class::write_cause;
|
||||||
|
csr_rd_cb[utvec] = &this_class::read_tvec;
|
||||||
|
}
|
||||||
|
if(FEAT & FEAT_CLIC) {
|
||||||
|
csr_rd_cb[mtvt] = &this_class::read_csr_reg;
|
||||||
|
csr_wr_cb[mtvt] = &this_class::write_csr_reg;
|
||||||
|
csr_rd_cb[mxnti] = &this_class::read_csr_reg;
|
||||||
|
csr_wr_cb[mxnti] = &this_class::write_csr_reg;
|
||||||
|
csr_rd_cb[mintstatus] = &this_class::read_csr_reg;
|
||||||
|
csr_wr_cb[mintstatus] = &this_class::write_null;
|
||||||
|
csr_rd_cb[mscratchcsw] = &this_class::read_csr_reg;
|
||||||
|
csr_wr_cb[mscratchcsw] = &this_class::write_csr_reg;
|
||||||
|
csr_rd_cb[mscratchcswl] = &this_class::read_csr_reg;
|
||||||
|
csr_wr_cb[mscratchcswl] = &this_class::write_csr_reg;
|
||||||
|
csr_rd_cb[mintthresh] = &this_class::read_csr_reg;
|
||||||
|
csr_wr_cb[mintthresh] = &this_class::write_intthresh;
|
||||||
|
csr_rd_cb[mclicbase] = &this_class::read_csr_reg;
|
||||||
|
csr_wr_cb[mclicbase] = &this_class::write_null;
|
||||||
|
|
||||||
|
clic_base_addr=0xC0000000;
|
||||||
|
clic_num_irq=16;
|
||||||
|
clic_int_reg.resize(clic_num_irq);
|
||||||
|
clic_cfg_reg=0x20;
|
||||||
|
clic_info_reg = (/*CLICINTCTLBITS*/ 4U<<21) + clic_num_irq;
|
||||||
|
mcause_max_irq=clic_num_irq+16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,6 +484,7 @@ template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_m
|
|||||||
if (sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file");
|
if (sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file");
|
||||||
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
||||||
if (reader.get_machine() != EM_RISCV) throw std::runtime_error("wrong elf machine in file");
|
if (reader.get_machine() != EM_RISCV) throw std::runtime_error("wrong elf machine in file");
|
||||||
|
auto entry = reader.get_entry();
|
||||||
for (const auto pseg : reader.segments) {
|
for (const auto pseg : reader.segments) {
|
||||||
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
||||||
const auto seg_data = pseg->get_data();
|
const auto seg_data = pseg->get_data();
|
||||||
@ -377,20 +497,90 @@ template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_m
|
|||||||
<< pseg->get_physical_address();
|
<< pseg->get_physical_address();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto sec : reader.sections) {
|
for(const auto sec : reader.sections) {
|
||||||
if (sec->get_name() == ".tohost") {
|
if(sec->get_name() == ".symtab") {
|
||||||
|
if ( SHT_SYMTAB == sec->get_type() ||
|
||||||
|
SHT_DYNSYM == sec->get_type() ) {
|
||||||
|
ELFIO::symbol_section_accessor symbols( reader, sec );
|
||||||
|
auto sym_no = symbols.get_symbols_num();
|
||||||
|
std::string name;
|
||||||
|
ELFIO::Elf64_Addr value = 0;
|
||||||
|
ELFIO::Elf_Xword size = 0;
|
||||||
|
unsigned char bind = 0;
|
||||||
|
unsigned char type = 0;
|
||||||
|
ELFIO::Elf_Half section = 0;
|
||||||
|
unsigned char other = 0;
|
||||||
|
for ( auto i = 0U; i < sym_no; ++i ) {
|
||||||
|
symbols.get_symbol( i, name, value, size, bind, type, section, other );
|
||||||
|
if(name=="tohost") {
|
||||||
|
tohost = value;
|
||||||
|
} else if(name=="fromhost") {
|
||||||
|
fromhost = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (sec->get_name() == ".tohost") {
|
||||||
tohost = sec->get_address();
|
tohost = sec->get_address();
|
||||||
fromhost = tohost + 0x40;
|
fromhost = tohost + 0x40;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(reader.get_entry(), true);
|
}
|
||||||
|
return std::make_pair(entry, true);
|
||||||
}
|
}
|
||||||
throw std::runtime_error("memory load file is not a valid elf file");
|
throw std::runtime_error("memory load file is not a valid elf file");
|
||||||
}
|
}
|
||||||
throw std::runtime_error("memory load file not found");
|
throw std::runtime_error("memory load file not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> bool riscv_hart_mu_p<BASE, FEAT>::pmp_check(const access_type type, const uint64_t addr, const unsigned len) {
|
||||||
|
constexpr auto PMP_SHIFT=2U;
|
||||||
|
constexpr auto PMP_R = 0x1U;
|
||||||
|
constexpr auto PMP_W = 0x2U;
|
||||||
|
constexpr auto PMP_X = 0x4U;
|
||||||
|
constexpr auto PMP_A = 0x18U;
|
||||||
|
constexpr auto PMP_L = 0x80U;
|
||||||
|
constexpr auto PMP_TOR =0x1U;
|
||||||
|
constexpr auto PMP_NA4 =0x2U;
|
||||||
|
constexpr auto PMP_NAPOT =0x3U;
|
||||||
|
reg_t base = 0;
|
||||||
|
for (size_t i = 0; i < 16; i++) {
|
||||||
|
reg_t tor = csr[pmpaddr0+i] << PMP_SHIFT;
|
||||||
|
uint8_t cfg = csr[pmpcfg0+(i/4)]>>(i%4);
|
||||||
|
if (cfg & PMP_A) {
|
||||||
|
auto pmp_a = (cfg & PMP_A) >> 3;
|
||||||
|
bool is_tor = pmp_a == PMP_TOR;
|
||||||
|
bool is_na4 = pmp_a == PMP_NA4;
|
||||||
|
|
||||||
|
reg_t mask = (csr[pmpaddr0+i] << 1) | (!is_na4);
|
||||||
|
mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
|
||||||
|
|
||||||
|
// Check each 4-byte sector of the access
|
||||||
|
bool any_match = false;
|
||||||
|
bool all_match = true;
|
||||||
|
for (reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) {
|
||||||
|
reg_t cur_addr = addr + offset;
|
||||||
|
bool napot_match = ((cur_addr ^ tor) & mask) == 0;
|
||||||
|
bool tor_match = base <= cur_addr && cur_addr < tor;
|
||||||
|
bool match = is_tor ? tor_match : napot_match;
|
||||||
|
any_match |= match;
|
||||||
|
all_match &= match;
|
||||||
|
}
|
||||||
|
if (any_match) {
|
||||||
|
// If the PMP matches only a strict subset of the access, fail it
|
||||||
|
if (!all_match)
|
||||||
|
return false;
|
||||||
|
return (this->reg.PRIV == PRIV_M && !(cfg & PMP_L)) ||
|
||||||
|
(type == access_type::READ && (cfg & PMP_R)) ||
|
||||||
|
(type == access_type::WRITE && (cfg & PMP_W)) ||
|
||||||
|
(type == access_type::FETCH && (cfg & PMP_X));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base = tor;
|
||||||
|
}
|
||||||
|
return this->reg.PRIV == PRIV_M;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT>
|
template <typename BASE, features_e FEAT>
|
||||||
iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const access_type access, const uint32_t space,
|
iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const access_type access, const uint32_t space,
|
||||||
const uint64_t addr, const unsigned length, uint8_t *const data) {
|
const uint64_t addr, const unsigned length, uint8_t *const data) {
|
||||||
@ -406,6 +596,14 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc
|
|||||||
try {
|
try {
|
||||||
switch (space) {
|
switch (space) {
|
||||||
case traits<BASE>::MEM: {
|
case traits<BASE>::MEM: {
|
||||||
|
if(FEAT & FEAT_PMP){
|
||||||
|
if(!pmp_check(access, addr, length) && (access&access_type::DEBUG) != access_type::DEBUG) {
|
||||||
|
fault_data = addr;
|
||||||
|
if (access && iss::access_type::DEBUG) throw trap_access(0, addr);
|
||||||
|
this->reg.trap_state = (1 << 31) | ((access==access_type::FETCH?1:5) << 16); // issue trap 1
|
||||||
|
return iss::Err;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) {
|
if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) {
|
||||||
fault_data = addr;
|
fault_data = addr;
|
||||||
if (access && iss::access_type::DEBUG) throw trap_access(0, addr);
|
if (access && iss::access_type::DEBUG) throw trap_access(0, addr);
|
||||||
@ -413,13 +611,27 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc
|
|||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
auto res = type==iss::address_type::PHYSICAL?
|
auto alignment = access == iss::access_type::FETCH? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length;
|
||||||
read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data):
|
if(alignment>1 && (addr&(alignment-1))){
|
||||||
read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
|
this->reg.trap_state = 1<<31 | 4<<16;
|
||||||
if (unlikely(res != iss::Ok)) this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
|
fault_data=addr;
|
||||||
|
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});
|
||||||
|
auto res = iss::Err;
|
||||||
|
if((FEAT & FEAT_CLIC) && access != access_type::FETCH && phys_addr.val>=clic_base_addr && (phys_addr.val+length)<=(clic_base_addr+0x5000)){ //TODO: should be a constant
|
||||||
|
res = read_clic(phys_addr.val, length, data);
|
||||||
|
} else {
|
||||||
|
res = read_mem( phys_addr, length, data);
|
||||||
|
}
|
||||||
|
if (unlikely(res != iss::Ok)){
|
||||||
|
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
|
||||||
|
fault_data=addr;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -445,6 +657,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,6 +691,14 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac
|
|||||||
try {
|
try {
|
||||||
switch (space) {
|
switch (space) {
|
||||||
case traits<BASE>::MEM: {
|
case traits<BASE>::MEM: {
|
||||||
|
if(FEAT & FEAT_PMP){
|
||||||
|
if(!pmp_check(access, addr, length) && (access&access_type::DEBUG) != access_type::DEBUG) {
|
||||||
|
fault_data = addr;
|
||||||
|
if (access && iss::access_type::DEBUG) throw trap_access(0, addr);
|
||||||
|
this->reg.trap_state = (1 << 31) | (7 << 16); // issue trap 1
|
||||||
|
return iss::Err;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) {
|
if (unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) {
|
||||||
fault_data = addr;
|
fault_data = addr;
|
||||||
if (access && iss::access_type::DEBUG) throw trap_access(0, addr);
|
if (access && iss::access_type::DEBUG) throw trap_access(0, addr);
|
||||||
@ -485,14 +706,22 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac
|
|||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
auto res = type==iss::address_type::PHYSICAL?
|
if(length>1 && (addr&(length-1)) && (access&access_type::DEBUG) != access_type::DEBUG){
|
||||||
write_mem(phys_addr_t{access, space, addr}, length, data):
|
this->reg.trap_state = 1<<31 | 6<<16;
|
||||||
write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
|
fault_data=addr;
|
||||||
if (unlikely(res != iss::Ok))
|
return iss::Err;
|
||||||
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 7 (Store/AMO access fault)
|
}
|
||||||
|
auto phys_addr = type==iss::address_type::PHYSICAL?phys_addr_t{access, space, addr}:BASE::v2p(iss::addr_t{access, type, space, addr});
|
||||||
|
auto res = ((FEAT & FEAT_CLIC) && phys_addr.val>=clic_base_addr && (phys_addr.val+length)<=(clic_base_addr+0x5000))? //TODO: should be a constant
|
||||||
|
write_clic(phys_addr.val, length, data) : write_mem( phys_addr, length, data);
|
||||||
|
if (unlikely(res != iss::Ok)) {
|
||||||
|
this->reg.trap_state = (1 << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)
|
||||||
|
fault_data=addr;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,6 +781,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
} catch (trap_access &ta) {
|
} catch (trap_access &ta) {
|
||||||
this->reg.trap_state = (1 << 31) | ta.id;
|
this->reg.trap_state = (1 << 31) | ta.id;
|
||||||
|
fault_data=ta.addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -580,12 +810,17 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
|
|||||||
return (this->*(it->second))(addr, val);
|
return (this->*(it->second))(addr, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_reg(unsigned addr, reg_t &val) {
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_csr_reg(unsigned addr, reg_t &val) {
|
||||||
val = csr[addr];
|
val = csr[addr];
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_reg(unsigned addr, reg_t val) {
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_null(unsigned addr, reg_t &val) {
|
||||||
|
val = 0;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_csr_reg(unsigned addr, reg_t val) {
|
||||||
csr[addr] = val;
|
csr[addr] = val;
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
@ -601,8 +836,50 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_cycle(unsigned addr, reg_t val) {
|
||||||
|
if (sizeof(typename traits<BASE>::reg_t) != 4) {
|
||||||
|
if (addr == mcycleh)
|
||||||
|
return iss::Err;
|
||||||
|
mcycle_csr = static_cast<uint64_t>(val);
|
||||||
|
} else {
|
||||||
|
if (addr == mcycle) {
|
||||||
|
mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val;
|
||||||
|
} else {
|
||||||
|
mcycle_csr = (static_cast<uint64_t>(val)<<32) + (mcycle_csr & 0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cycle_offset = mcycle_csr-this->reg.icount; // TODO: relying on wrap-around
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_instret(unsigned addr, reg_t &val) {
|
||||||
|
if ((addr&0xff) == (minstret&0xff)) {
|
||||||
|
val = static_cast<reg_t>(this->reg.instret);
|
||||||
|
} else if ((addr&0xff) == (minstreth&0xff)) {
|
||||||
|
if (sizeof(typename traits<BASE>::reg_t) != 4) return iss::Err;
|
||||||
|
val = static_cast<reg_t>(this->reg.instret >> 32);
|
||||||
|
}
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_instret(unsigned addr, reg_t val) {
|
||||||
|
if (sizeof(typename traits<BASE>::reg_t) != 4) {
|
||||||
|
if ((addr&0xff) == (minstreth&0xff))
|
||||||
|
return iss::Err;
|
||||||
|
this->reg.instret = static_cast<uint64_t>(val);
|
||||||
|
} else {
|
||||||
|
if ((addr&0xff) == (minstret&0xff)) {
|
||||||
|
this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val;
|
||||||
|
} else {
|
||||||
|
this->reg.instret = (static_cast<uint64_t>(val)<<32) + (this->reg.instret & 0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->reg.instret--;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_time(unsigned addr, reg_t &val) {
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_time(unsigned addr, reg_t &val) {
|
||||||
uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052;
|
uint64_t time_val = this->reg.icount / (100000000 / 32768 - 1); //-> ~3052;
|
||||||
if (addr == time) {
|
if (addr == time) {
|
||||||
val = static_cast<reg_t>(time_val);
|
val = static_cast<reg_t>(time_val);
|
||||||
} else if (addr == timeh) {
|
} else if (addr == timeh) {
|
||||||
@ -612,6 +889,10 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_tvec(unsigned addr, reg_t &val) {
|
||||||
|
val = csr[addr] & ~2;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_status(unsigned addr, reg_t &val) {
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_status(unsigned addr, reg_t &val) {
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
auto req_priv_lvl = (addr >> 8) & 0x3;
|
||||||
val = state.mstatus & hart_state_type::get_mask(req_priv_lvl);
|
val = state.mstatus & hart_state_type::get_mask(req_priv_lvl);
|
||||||
@ -625,9 +906,8 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_ie(unsigned addr, reg_t &val) {
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_cause(unsigned addr, reg_t val) {
|
||||||
val = csr[mie];
|
csr[addr] = val & ((1UL<<(traits<BASE>::XLEN-1))|(mcause_max_irq-1)); //TODO: make exception code size configurable
|
||||||
val &= csr[mideleg];
|
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,32 +916,66 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_ie(unsigned addr, reg_t &val) {
|
||||||
|
auto mask = get_irq_wrmask((addr >> 8) & 0x3);
|
||||||
|
val = csr[mie] & mask;
|
||||||
|
if(this->reg.PRIV!=3)
|
||||||
|
val &= csr[mideleg];
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_ie(unsigned addr, reg_t val) {
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_ie(unsigned addr, reg_t val) {
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
auto mask = get_irq_wrmask((addr >> 8) & 0x3);
|
||||||
auto mask = get_irq_mask(req_priv_lvl);
|
if(this->reg.PRIV==0)
|
||||||
|
mask&= ~(0xff<<4); // STIE and UTIE are read only in user and supervisor mode
|
||||||
csr[mie] = (csr[mie] & ~mask) | (val & mask);
|
csr[mie] = (csr[mie] & ~mask) | (val & mask);
|
||||||
check_interrupt();
|
check_interrupt();
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_ip(unsigned addr, reg_t &val) {
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_ip(unsigned addr, reg_t &val) {
|
||||||
val = csr[mip];
|
auto mask = get_irq_wrmask((addr >> 8) & 0x3);
|
||||||
val &= csr[mideleg];
|
val = csr[mip] & mask;
|
||||||
|
if(this->reg.PRIV!=3)
|
||||||
|
val &= csr[mideleg];
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_ip(unsigned addr, reg_t val) {
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_ip(unsigned addr, reg_t val) {
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
auto mask = get_irq_wrmask((addr >> 8) & 0x3);
|
||||||
auto mask = get_irq_mask(req_priv_lvl);
|
mask &= ~(8 << 4); // MTIP is read only
|
||||||
mask &= ~(1 << 7); // MTIP is read only
|
if(this->reg.PRIV!=3)
|
||||||
|
mask &= ~(3 << 4); // STIP and UTIP are read only in user and supervisor mode
|
||||||
csr[mip] = (csr[mip] & ~mask) | (val & mask);
|
csr[mip] = (csr[mip] & ~mask) | (val & mask);
|
||||||
check_interrupt();
|
check_interrupt();
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_ideleg(unsigned addr, reg_t val) {
|
||||||
|
auto mask = 0b000100010001; // only U mode supported
|
||||||
|
csr[mideleg] = (csr[mideleg] & ~mask) | (val & mask);
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_edeleg(unsigned addr, reg_t val) {
|
||||||
|
auto mask = 0b1011001111110111; // bit 14/10 (reserved), bit 11 (Env call), and 3 (break) are hardwired to 0
|
||||||
|
csr[medeleg] = (csr[medeleg] & ~mask) | (val & mask);
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_epc(unsigned addr, reg_t val) {
|
||||||
|
csr[addr] = val & get_pc_mask();
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BASE, features_e FEAT>
|
||||||
|
iss::status riscv_hart_mu_p<BASE, FEAT>::write_intthresh(unsigned addr, reg_t val) {
|
||||||
|
csr[addr]= val &0xff;
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT>
|
template <typename BASE, features_e FEAT>
|
||||||
iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
|
iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
|
||||||
if(mem_read_cb) return mem_read_cb(paddr, length, data);
|
if(mem_read_cb) return mem_read_cb(paddr, length, data);
|
||||||
switch (paddr.val) {
|
switch (paddr.val) {
|
||||||
case 0x0200BFF8: { // CLINT base, mtime reg
|
case 0x0200BFF8: { // CLINT base, mtime reg
|
||||||
@ -677,9 +991,9 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned le
|
|||||||
if (this->reg.icount > 30000) data[3] |= 0x80;
|
if (this->reg.icount > 30000) data[3] |= 0x80;
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
const auto &p = mem(paddr.val / mem.page_size);
|
for(auto offs=0U; offs<length; ++offs) {
|
||||||
auto offs = paddr.val & mem.page_addr_mask;
|
*(data + offs)=mem[(paddr.val+offs)%mem.size()];
|
||||||
std::copy(p.data() + offs, p.data() + offs + length, data);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
@ -687,7 +1001,6 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned le
|
|||||||
|
|
||||||
template <typename BASE, features_e FEAT>
|
template <typename BASE, features_e FEAT>
|
||||||
iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
|
iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
|
||||||
if ((paddr.val + length) > mem.size()) return iss::Err;
|
|
||||||
if(mem_write_cb) return mem_write_cb(paddr, length, data);
|
if(mem_write_cb) return mem_write_cb(paddr, length, data);
|
||||||
switch (paddr.val) {
|
switch (paddr.val) {
|
||||||
case 0x10013000: // UART0 base, TXFIFO reg
|
case 0x10013000: // UART0 base, TXFIFO reg
|
||||||
@ -719,9 +1032,10 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l
|
|||||||
std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask));
|
std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask));
|
||||||
// tohost handling in case of riscv-test
|
// tohost handling in case of riscv-test
|
||||||
if (paddr.access && iss::access_type::FUNC) {
|
if (paddr.access && iss::access_type::FUNC) {
|
||||||
auto tohost_upper = (traits<BASE>::XLEN == 32 && paddr.val == (tohost + 4));
|
auto tohost_upper = (traits<BASE>::XLEN == 32 && paddr.val == (tohost + 4)) ||
|
||||||
|
(traits<BASE>::XLEN == 64 && paddr.val == tohost);
|
||||||
auto tohost_lower =
|
auto tohost_lower =
|
||||||
(traits<BASE>::XLEN == 32 && paddr.val == tohost);
|
(traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
|
||||||
if (tohost_lower || tohost_upper) {
|
if (tohost_lower || tohost_upper) {
|
||||||
uint64_t hostvar = *reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask));
|
uint64_t hostvar = *reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask));
|
||||||
if (tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
|
if (tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
|
||||||
@ -752,7 +1066,8 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l
|
|||||||
}
|
}
|
||||||
} else if (tohost_lower)
|
} else if (tohost_lower)
|
||||||
to_host_wr_cnt++;
|
to_host_wr_cnt++;
|
||||||
} else if (traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) {
|
} else if ((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) ||
|
||||||
|
(traits<BASE>::XLEN == 64 && paddr.val == fromhost)) {
|
||||||
uint64_t fhostvar = *reinterpret_cast<uint64_t *>(p.data() + (fromhost & mem.page_addr_mask));
|
uint64_t fhostvar = *reinterpret_cast<uint64_t *>(p.data() + (fromhost & mem.page_addr_mask));
|
||||||
*reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
|
*reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
|
||||||
}
|
}
|
||||||
@ -762,6 +1077,84 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l
|
|||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void read_uint32(uint64_t offs, uint32_t& reg, uint8_t *const data, unsigned length) {
|
||||||
|
auto reg_ptr = reinterpret_cast<uint8_t*>(®);
|
||||||
|
switch (offs & 0x3) {
|
||||||
|
case 0:
|
||||||
|
for (auto i = 0U; i < length; ++i)
|
||||||
|
*(data + i) = *(reg_ptr + i);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
for (auto i = 0U; i < length; ++i)
|
||||||
|
*(data + i) = *(reg_ptr + 1 + i);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
for (auto i = 0U; i < length; ++i)
|
||||||
|
*(data + i) = *(reg_ptr + 2 + i);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
*data = *(reg_ptr + 3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_uint32(uint64_t offs, uint32_t& reg, const uint8_t *const data, unsigned length) {
|
||||||
|
auto reg_ptr = reinterpret_cast<uint8_t*>(®);
|
||||||
|
switch (offs & 0x3) {
|
||||||
|
case 0:
|
||||||
|
for (auto i = 0U; i < length; ++i)
|
||||||
|
*(reg_ptr + i) = *(data + i);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
for (auto i = 0U; i < length; ++i)
|
||||||
|
*(reg_ptr + 1 + i) = *(data + i);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
for (auto i = 0U; i < length; ++i)
|
||||||
|
*(reg_ptr + 2 + i) = *(data + i);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
*(reg_ptr + 3) = *data ;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BASE, features_e FEAT>
|
||||||
|
iss::status riscv_hart_mu_p<BASE, FEAT>::read_clic(uint64_t addr, unsigned length, uint8_t *const data) {
|
||||||
|
if(addr==clic_base_addr) { // cliccfg
|
||||||
|
*data=clic_cfg_reg;
|
||||||
|
for(auto i=1; i<length; ++i) *(data+i)=0;
|
||||||
|
} else if(addr>=(clic_base_addr+4) && (addr+length)<=(clic_base_addr+8)){ // clicinfo
|
||||||
|
read_uint32(addr, clic_info_reg, data, length);
|
||||||
|
} else if(addr>=(clic_base_addr+0x40) && (addr+length)<=(clic_base_addr+0x40+clic_num_trigger*4)){ // clicinttrig
|
||||||
|
auto offset = ((addr&0x7fff)-0x40)/4;
|
||||||
|
read_uint32(addr, clic_inttrig_reg[offset], data, length);
|
||||||
|
} else if(addr>=(clic_base_addr+0x1000) && (addr+length)<=(clic_base_addr+clic_num_irq*4)){ // clicintip/clicintie/clicintattr/clicintctl
|
||||||
|
auto offset = ((addr&0x7fff)-0x1000)/4;
|
||||||
|
read_uint32(addr, clic_int_reg[offset].raw, data, length);
|
||||||
|
} else {
|
||||||
|
for(auto i = 0U; i<length; ++i) *(data+i)=0;
|
||||||
|
}
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BASE, features_e FEAT>
|
||||||
|
iss::status riscv_hart_mu_p<BASE, FEAT>::write_clic(uint64_t addr, unsigned length, const uint8_t *const data) {
|
||||||
|
if(addr==clic_base_addr) { // cliccfg
|
||||||
|
clic_cfg_reg = *data;
|
||||||
|
clic_cfg_reg&= 0x7f;
|
||||||
|
// } else if(addr>=(clic_base_addr+4) && (addr+length)<=(clic_base_addr+4)){ // clicinfo
|
||||||
|
// write_uint32(addr, clic_info_reg, data, length);
|
||||||
|
} else if(addr>=(clic_base_addr+0x40) && (addr+length)<=(clic_base_addr+0xC0)){ // clicinttrig
|
||||||
|
auto offset = ((addr&0x7fff)-0x40)/4;
|
||||||
|
write_uint32(addr, clic_inttrig_reg[offset], data, length);
|
||||||
|
} else if(addr>=(clic_base_addr+0x1000) && (addr+length)<=(clic_base_addr+clic_num_irq*4)){ // clicintip/clicintie/clicintattr/clicintctl
|
||||||
|
auto offset = ((addr&0x7fff)-0x1000)/4;
|
||||||
|
write_uint32(addr, clic_int_reg[offset].raw, data, length);
|
||||||
|
}
|
||||||
|
return iss::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> inline void riscv_hart_mu_p<BASE, FEAT>::reset(uint64_t address) {
|
template <typename BASE, features_e FEAT> inline void riscv_hart_mu_p<BASE, FEAT>::reset(uint64_t address) {
|
||||||
BASE::reset(address);
|
BASE::reset(address);
|
||||||
state.mstatus = hart_state_type::mstatus_reset_val;
|
state.mstatus = hart_state_type::mstatus_reset_val;
|
||||||
@ -776,7 +1169,7 @@ template <typename BASE, features_e FEAT> void riscv_hart_mu_p<BASE, FEAT>::chec
|
|||||||
auto ena_irq = csr[mip] & csr[mie];
|
auto ena_irq = csr[mip] & csr[mie];
|
||||||
|
|
||||||
bool mie = state.mstatus.MIE;
|
bool mie = state.mstatus.MIE;
|
||||||
auto m_enabled = this->reg.PRIV < PRIV_M || (this->reg.PRIV == PRIV_M && mie);
|
auto m_enabled = this->reg.PRIV < PRIV_M || mie;
|
||||||
auto enabled_interrupts = m_enabled ? ena_irq & ~ideleg : 0;
|
auto enabled_interrupts = m_enabled ? ena_irq & ~ideleg : 0;
|
||||||
|
|
||||||
if (enabled_interrupts != 0) {
|
if (enabled_interrupts != 0) {
|
||||||
@ -789,9 +1182,10 @@ template <typename BASE, features_e FEAT> void riscv_hart_mu_p<BASE, FEAT>::chec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::enter_trap(uint64_t flags, uint64_t addr) {
|
template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) {
|
||||||
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
|
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
|
||||||
// calculate and write mcause val
|
// calculate and write mcause val
|
||||||
|
if(flags==std::numeric_limits<uint64_t>::max()) flags=this->reg.trap_state;
|
||||||
auto trap_id = bit_sub<0, 16>(flags);
|
auto trap_id = bit_sub<0, 16>(flags);
|
||||||
auto cause = bit_sub<16, 15>(flags);
|
auto cause = bit_sub<16, 15>(flags);
|
||||||
if (trap_id == 0 && cause == 11) cause = 0x8 + this->reg.PRIV; // adjust environment call cause
|
if (trap_id == 0 && cause == 11) cause = 0x8 + this->reg.PRIV; // adjust environment call cause
|
||||||
@ -809,7 +1203,7 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::
|
|||||||
* access, or page-fault exception occurs, mtval is written with the
|
* access, or page-fault exception occurs, mtval is written with the
|
||||||
* faulting effective address.
|
* faulting effective address.
|
||||||
*/
|
*/
|
||||||
csr[utval | (new_priv << 8)] = fault_data;
|
csr[utval | (new_priv << 8)] = cause==2?((instr & 0x3)==3?instr:instr&0xffff):fault_data;
|
||||||
fault_data = 0;
|
fault_data = 0;
|
||||||
} else {
|
} else {
|
||||||
if (this->reg.PRIV != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)
|
if (this->reg.PRIV != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)
|
||||||
@ -844,11 +1238,8 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::
|
|||||||
auto ivec = csr[utvec | (new_priv << 8)];
|
auto ivec = csr[utvec | (new_priv << 8)];
|
||||||
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
||||||
// bits in mtvec
|
// bits in mtvec
|
||||||
this->reg.NEXT_PC = ivec & ~0x1UL;
|
this->reg.NEXT_PC = ivec & ~0x3UL;
|
||||||
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
|
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
|
||||||
// reset trap state
|
|
||||||
this->reg.PRIV = new_priv;
|
|
||||||
this->reg.trap_state = 0;
|
|
||||||
std::array<char, 32> buffer;
|
std::array<char, 32> buffer;
|
||||||
sprintf(buffer.data(), "0x%016lx", addr);
|
sprintf(buffer.data(), "0x%016lx", addr);
|
||||||
if((flags&0xffffffff) != 0xffffffff)
|
if((flags&0xffffffff) != 0xffffffff)
|
||||||
@ -856,6 +1247,9 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::
|
|||||||
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
|
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
|
||||||
<< " at address " << buffer.data() << " occurred, changing privilege level from "
|
<< " at address " << buffer.data() << " occurred, changing privilege level from "
|
||||||
<< lvl[this->reg.PRIV] << " to " << lvl[new_priv];
|
<< lvl[this->reg.PRIV] << " to " << lvl[new_priv];
|
||||||
|
// reset trap state
|
||||||
|
this->reg.PRIV = new_priv;
|
||||||
|
this->reg.trap_state = 0;
|
||||||
return this->reg.NEXT_PC;
|
return this->reg.NEXT_PC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,16 +1264,19 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::
|
|||||||
this->reg.PRIV = state.mstatus.MPP;
|
this->reg.PRIV = state.mstatus.MPP;
|
||||||
state.mstatus.MPP = 0; // clear mpp to U mode
|
state.mstatus.MPP = 0; // clear mpp to U mode
|
||||||
state.mstatus.MIE = state.mstatus.MPIE;
|
state.mstatus.MIE = state.mstatus.MPIE;
|
||||||
|
state.mstatus.MPIE = 1;
|
||||||
break;
|
break;
|
||||||
case PRIV_U:
|
case PRIV_U:
|
||||||
this->reg.PRIV = 0;
|
this->reg.PRIV = 0;
|
||||||
state.mstatus.UIE = state.mstatus.UPIE;
|
state.mstatus.UIE = state.mstatus.UPIE;
|
||||||
|
state.mstatus.UPIE = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// sets the pc to the value stored in the x epc register.
|
// sets the pc to the value stored in the x epc register.
|
||||||
this->reg.NEXT_PC = csr[uepc | inst_priv << 8];
|
this->reg.NEXT_PC = csr[uepc | inst_priv << 8];
|
||||||
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to "
|
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to "
|
||||||
<< lvl[this->reg.PRIV];
|
<< lvl[this->reg.PRIV];
|
||||||
|
check_interrupt();
|
||||||
return this->reg.NEXT_PC;
|
return this->reg.NEXT_PC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ template <> struct traits<tgc_c> {
|
|||||||
static constexpr std::array<const char*, 35> reg_aliases{
|
static constexpr std::array<const char*, 35> reg_aliases{
|
||||||
{"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"}};
|
{"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"}};
|
||||||
|
|
||||||
enum constants {XLEN=32, PCLEN=32, MISA_VAL=0b01000000000000000001000100000100, PGSIZE=0x1000, PGMASK=0b111111111111, CSR_SIZE=4096, fence=0, fencei=1, fencevmal=2, fencevmau=3, eei_aligned_addresses=1, MUL_LEN=64};
|
enum constants {XLEN=32, PCLEN=32, MISA_VAL=0b01000000000000000001000100000100, PGSIZE=0x1000, PGMASK=0b111111111111, CSR_SIZE=4096, fence=0, fencei=1, fencevmal=2, fencevmau=3, MUL_LEN=64};
|
||||||
|
|
||||||
constexpr static unsigned FP_REGS_SIZE = 0;
|
constexpr static unsigned FP_REGS_SIZE = 0;
|
||||||
|
|
||||||
@ -61,7 +61,9 @@ template <> struct traits<tgc_c> {
|
|||||||
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, NUM_REGS,
|
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, NUM_REGS,
|
||||||
TRAP_STATE=NUM_REGS,
|
TRAP_STATE=NUM_REGS,
|
||||||
PENDING_TRAP,
|
PENDING_TRAP,
|
||||||
ICOUNT
|
ICOUNT,
|
||||||
|
CYCLE,
|
||||||
|
INSTRET
|
||||||
};
|
};
|
||||||
|
|
||||||
using reg_t = uint32_t;
|
using reg_t = uint32_t;
|
||||||
@ -74,11 +76,11 @@ template <> struct traits<tgc_c> {
|
|||||||
|
|
||||||
using phys_addr_t = iss::typed_addr_t<iss::address_type::PHYSICAL>;
|
using phys_addr_t = iss::typed_addr_t<iss::address_type::PHYSICAL>;
|
||||||
|
|
||||||
static constexpr std::array<const uint32_t, 38> reg_bit_widths{
|
static constexpr std::array<const uint32_t, 40> reg_bit_widths{
|
||||||
{32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,8,32,32,64}};
|
{32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,8,32,32,64,64,64}};
|
||||||
|
|
||||||
static constexpr std::array<const uint32_t, 38> reg_byte_offsets{
|
static constexpr std::array<const uint32_t, 40> reg_byte_offsets{
|
||||||
{0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,137,141,145}};
|
{0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,137,141,145,153,161}};
|
||||||
|
|
||||||
static const uint64_t addr_mask = (reg_t(1) << (XLEN - 1)) | ((reg_t(1) << (XLEN - 1)) - 1);
|
static const uint64_t addr_mask = (reg_t(1) << (XLEN - 1)) | ((reg_t(1) << (XLEN - 1)) - 1);
|
||||||
|
|
||||||
@ -154,25 +156,27 @@ template <> struct traits<tgc_c> {
|
|||||||
CLI = 64,
|
CLI = 64,
|
||||||
CLUI = 65,
|
CLUI = 65,
|
||||||
CADDI16SP = 66,
|
CADDI16SP = 66,
|
||||||
CSRLI = 67,
|
__reserved_clui = 67,
|
||||||
CSRAI = 68,
|
CSRLI = 68,
|
||||||
CANDI = 69,
|
CSRAI = 69,
|
||||||
CSUB = 70,
|
CANDI = 70,
|
||||||
CXOR = 71,
|
CSUB = 71,
|
||||||
COR = 72,
|
CXOR = 72,
|
||||||
CAND = 73,
|
COR = 73,
|
||||||
CJ = 74,
|
CAND = 74,
|
||||||
CBEQZ = 75,
|
CJ = 75,
|
||||||
CBNEZ = 76,
|
CBEQZ = 76,
|
||||||
CSLLI = 77,
|
CBNEZ = 77,
|
||||||
CLWSP = 78,
|
CSLLI = 78,
|
||||||
CMV = 79,
|
CLWSP = 79,
|
||||||
CJR = 80,
|
CMV = 80,
|
||||||
CADD = 81,
|
CJR = 81,
|
||||||
CJALR = 82,
|
__reserved_cmv = 82,
|
||||||
CEBREAK = 83,
|
CADD = 83,
|
||||||
CSWSP = 84,
|
CJALR = 84,
|
||||||
DII = 85,
|
CEBREAK = 85,
|
||||||
|
CSWSP = 86,
|
||||||
|
DII = 87,
|
||||||
MAX_OPCODE
|
MAX_OPCODE
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -259,6 +263,8 @@ protected:
|
|||||||
uint8_t PRIV = 0;
|
uint8_t PRIV = 0;
|
||||||
uint32_t trap_state = 0, pending_trap = 0;
|
uint32_t trap_state = 0, pending_trap = 0;
|
||||||
uint64_t icount = 0;
|
uint64_t icount = 0;
|
||||||
|
uint64_t cycle = 0;
|
||||||
|
uint64_t instret = 0;
|
||||||
uint32_t last_branch;
|
uint32_t last_branch;
|
||||||
} reg;
|
} reg;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
@ -42,7 +42,7 @@ using vm_ptr= std::unique_ptr<iss::vm_if>;
|
|||||||
|
|
||||||
template<typename PLAT>
|
template<typename PLAT>
|
||||||
std::tuple<cpu_ptr, vm_ptr> create_cpu(std::string const& backend, unsigned gdb_port){
|
std::tuple<cpu_ptr, vm_ptr> create_cpu(std::string const& backend, unsigned gdb_port){
|
||||||
using core_type = typename PLAT::super;
|
using core_type = typename PLAT::core;
|
||||||
core_type* lcpu = new PLAT();
|
core_type* lcpu = new PLAT();
|
||||||
if(backend == "interp")
|
if(backend == "interp")
|
||||||
return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(lcpu, gdb_port)}};
|
return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(lcpu, gdb_port)}};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
|
* Copyright (C) 2017-2021 MINRES Technologies GmbH
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -30,23 +30,23 @@
|
|||||||
*
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
#ifndef _SYSC_SIFIVE_FE310_H_
|
#ifndef _SYSC_CORE_COMPLEX_H_
|
||||||
#define _SYSC_SIFIVE_FE310_H_
|
#define _SYSC_CORE_COMPLEX_H_
|
||||||
|
|
||||||
#include "tlm/scc/initiator_mixin.h"
|
#include <tlm/scc/initiator_mixin.h>
|
||||||
#include "scc/traceable.h"
|
#include <scc/traceable.h>
|
||||||
#include "scc/utilities.h"
|
#include <scc/tick2time.h>
|
||||||
#include "tlm/scc/scv/tlm_rec_initiator_socket.h"
|
#include <scc/utilities.h>
|
||||||
|
#include <tlm/scc/scv/tlm_rec_initiator_socket.h>
|
||||||
|
#ifdef CWR_SYSTEMC
|
||||||
|
#include <scmlinc/scml_property.h>
|
||||||
|
#else
|
||||||
#include <cci_configuration>
|
#include <cci_configuration>
|
||||||
|
#endif
|
||||||
#include <tlm>
|
#include <tlm>
|
||||||
#include <tlm_core/tlm_1/tlm_req_rsp/tlm_1_interfaces/tlm_core_ifs.h>
|
|
||||||
#include <tlm_utils/tlm_quantumkeeper.h>
|
#include <tlm_utils/tlm_quantumkeeper.h>
|
||||||
#include <util/range_lut.h>
|
#include <util/range_lut.h>
|
||||||
|
#include <memory>
|
||||||
class scv_tr_db;
|
|
||||||
class scv_tr_stream;
|
|
||||||
struct _scv_tr_generator_default_data;
|
|
||||||
template <class T_begin, class T_end> class scv_tr_generator;
|
|
||||||
|
|
||||||
namespace sysc {
|
namespace sysc {
|
||||||
|
|
||||||
@ -62,13 +62,12 @@ public:
|
|||||||
|
|
||||||
namespace tgfs {
|
namespace tgfs {
|
||||||
class core_wrapper;
|
class core_wrapper;
|
||||||
|
struct core_trace;
|
||||||
|
|
||||||
class core_complex : public sc_core::sc_module, public scc::traceable {
|
class core_complex : public sc_core::sc_module, public scc::traceable {
|
||||||
public:
|
public:
|
||||||
tlm::scc::initiator_mixin<tlm::scc::scv::tlm_rec_initiator_socket<32>> initiator{"intor"};
|
tlm::scc::initiator_mixin<tlm::scc::scv::tlm_rec_initiator_socket<32>> initiator{"intor"};
|
||||||
|
|
||||||
sc_core::sc_in<sc_core::sc_time> clk_i{"clk_i"};
|
|
||||||
|
|
||||||
sc_core::sc_in<bool> rst_i{"rst_i"};
|
sc_core::sc_in<bool> rst_i{"rst_i"};
|
||||||
|
|
||||||
sc_core::sc_in<bool> global_irq_i{"global_irq_i"};
|
sc_core::sc_in<bool> global_irq_i{"global_irq_i"};
|
||||||
@ -79,6 +78,9 @@ public:
|
|||||||
|
|
||||||
sc_core::sc_vector<sc_core::sc_in<bool>> local_irq_i{"local_irq_i", 16};
|
sc_core::sc_vector<sc_core::sc_in<bool>> local_irq_i{"local_irq_i", 16};
|
||||||
|
|
||||||
|
#ifndef CWR_SYSTEMC
|
||||||
|
sc_core::sc_in<sc_core::sc_time> clk_i{"clk_i"};
|
||||||
|
|
||||||
sc_core::sc_port<tlm::tlm_peek_if<uint64_t>, 1, sc_core::SC_ZERO_OR_MORE_BOUND> mtime_o;
|
sc_core::sc_port<tlm::tlm_peek_if<uint64_t>, 1, sc_core::SC_ZERO_OR_MORE_BOUND> mtime_o;
|
||||||
|
|
||||||
cci::cci_param<std::string> elf_file{"elf_file", ""};
|
cci::cci_param<std::string> elf_file{"elf_file", ""};
|
||||||
@ -97,7 +99,47 @@ public:
|
|||||||
|
|
||||||
cci::cci_param<uint32_t> mhartid{"mhartid", 0};
|
cci::cci_param<uint32_t> mhartid{"mhartid", 0};
|
||||||
|
|
||||||
core_complex(sc_core::sc_module_name name);
|
core_complex(sc_core::sc_module_name const& name);
|
||||||
|
|
||||||
|
#else
|
||||||
|
sc_core::sc_in<bool> clk_i{"clk_i"};
|
||||||
|
|
||||||
|
sc_core::sc_in<uint64_t> mtime_i{"mtime_i"};
|
||||||
|
|
||||||
|
scml_property<std::string> elf_file{"elf_file", ""};
|
||||||
|
|
||||||
|
scml_property<bool> enable_disass{"enable_disass", false};
|
||||||
|
|
||||||
|
scml_property<unsigned long long> reset_address{"reset_address", 0ULL};
|
||||||
|
|
||||||
|
scml_property<std::string> core_type{"core_type", "tgc_c"};
|
||||||
|
|
||||||
|
scml_property<std::string> backend{"backend", "interp"};
|
||||||
|
|
||||||
|
scml_property<unsigned> gdb_server_port{"gdb_server_port", 0};
|
||||||
|
|
||||||
|
scml_property<bool> dump_ir{"dump_ir", false};
|
||||||
|
|
||||||
|
scml_property<uint32_t> mhartid{"mhartid", 0};
|
||||||
|
|
||||||
|
core_complex(sc_core::sc_module_name const& name)
|
||||||
|
: sc_module(name)
|
||||||
|
, local_irq_i{"local_irq_i", 16}
|
||||||
|
, elf_file{"elf_file", ""}
|
||||||
|
, enable_disass{"enable_disass", false}
|
||||||
|
, reset_address{"reset_address", 0ULL}
|
||||||
|
, core_type{"core_type", "tgc_c"}
|
||||||
|
, backend{"backend", "interp"}
|
||||||
|
, gdb_server_port{"gdb_server_port", 0}
|
||||||
|
, dump_ir{"dump_ir", false}
|
||||||
|
, mhartid{"mhartid", 0}
|
||||||
|
, read_lut(tlm_dmi_ext())
|
||||||
|
, write_lut(tlm_dmi_ext())
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
~core_complex();
|
~core_complex();
|
||||||
|
|
||||||
@ -121,13 +163,14 @@ public:
|
|||||||
|
|
||||||
void trace(sc_core::sc_trace_file *trf) const override;
|
void trace(sc_core::sc_trace_file *trf) const override;
|
||||||
|
|
||||||
void disass_output(uint64_t pc, const std::string instr);
|
bool disass_output(uint64_t pc, const std::string instr);
|
||||||
|
|
||||||
|
void set_clock_period(sc_core::sc_time period);
|
||||||
protected:
|
protected:
|
||||||
void before_end_of_elaboration() override;
|
void before_end_of_elaboration() override;
|
||||||
void start_of_simulation() override;
|
void start_of_simulation() override;
|
||||||
|
void forward();
|
||||||
void run();
|
void run();
|
||||||
void clk_cb();
|
|
||||||
void rst_cb();
|
void rst_cb();
|
||||||
void sw_irq_cb();
|
void sw_irq_cb();
|
||||||
void timer_irq_cb();
|
void timer_irq_cb();
|
||||||
@ -136,21 +179,14 @@ protected:
|
|||||||
util::range_lut<tlm_dmi_ext> read_lut, write_lut;
|
util::range_lut<tlm_dmi_ext> read_lut, write_lut;
|
||||||
tlm_utils::tlm_quantumkeeper quantum_keeper;
|
tlm_utils::tlm_quantumkeeper quantum_keeper;
|
||||||
std::vector<uint8_t> write_buf;
|
std::vector<uint8_t> write_buf;
|
||||||
std::unique_ptr<core_wrapper> cpu;
|
core_wrapper* cpu{nullptr};
|
||||||
sc_core::sc_time curr_clk;
|
sc_core::sc_signal<sc_core::sc_time> curr_clk;
|
||||||
#ifdef WITH_SCV
|
core_trace* trc{nullptr};
|
||||||
//! transaction recording database
|
std::unique_ptr<scc::tick2time> t2t;
|
||||||
scv_tr_db *m_db;
|
private:
|
||||||
//! blocking transaction recording stream handle
|
void init();
|
||||||
scv_tr_stream *stream_handle;
|
|
||||||
//! transaction generator handle for blocking transactions
|
|
||||||
scv_tr_generator<_scv_tr_generator_default_data, _scv_tr_generator_default_data> *instr_tr_handle;
|
|
||||||
scv_tr_generator<uint64_t, _scv_tr_generator_default_data> *fetch_tr_handle;
|
|
||||||
scv_tr_handle tr_handle;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace SiFive */
|
} /* namespace SiFive */
|
||||||
} /* namespace sysc */
|
} /* namespace sysc */
|
||||||
|
|
||||||
#endif /* _SYSC_SIFIVE_FE310_H_ */
|
#endif /* _SYSC_CORE_COMPLEX_H_ */
|
||||||
|
@ -49,7 +49,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
/*----------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
#ifdef __GNUC__
|
||||||
#define SOFTFLOAT_BUILTIN_CLZ 1
|
#define SOFTFLOAT_BUILTIN_CLZ 1
|
||||||
#define SOFTFLOAT_INTRINSIC_INT128 1
|
#define SOFTFLOAT_INTRINSIC_INT128 1
|
||||||
|
#endif
|
||||||
#include "opts-GCC.h"
|
#include "opts-GCC.h"
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ using namespace iss::arch;
|
|||||||
|
|
||||||
constexpr std::array<const char*, 35> iss::arch::traits<iss::arch::tgc_c>::reg_names;
|
constexpr std::array<const char*, 35> iss::arch::traits<iss::arch::tgc_c>::reg_names;
|
||||||
constexpr std::array<const char*, 35> iss::arch::traits<iss::arch::tgc_c>::reg_aliases;
|
constexpr std::array<const char*, 35> iss::arch::traits<iss::arch::tgc_c>::reg_aliases;
|
||||||
constexpr std::array<const uint32_t, 38> iss::arch::traits<iss::arch::tgc_c>::reg_bit_widths;
|
constexpr std::array<const uint32_t, 40> iss::arch::traits<iss::arch::tgc_c>::reg_bit_widths;
|
||||||
constexpr std::array<const uint32_t, 38> iss::arch::traits<iss::arch::tgc_c>::reg_byte_offsets;
|
constexpr std::array<const uint32_t, 40> iss::arch::traits<iss::arch::tgc_c>::reg_byte_offsets;
|
||||||
|
|
||||||
tgc_c::tgc_c() {
|
tgc_c::tgc_c() {
|
||||||
reg.icount = 0;
|
reg.icount = 0;
|
||||||
|
16
src/main.cpp
16
src/main.cpp
@ -47,7 +47,7 @@ using tgc_b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_b>;
|
|||||||
#ifdef CORE_TGC_D
|
#ifdef CORE_TGC_D
|
||||||
#include "iss/arch/riscv_hart_mu_p.h"
|
#include "iss/arch/riscv_hart_mu_p.h"
|
||||||
#include "iss/arch/tgc_d.h"
|
#include "iss/arch/tgc_d.h"
|
||||||
using tgc_d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d, iss::arch::FEAT_PMP>;
|
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)>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef WITH_LLVM
|
#ifdef WITH_LLVM
|
||||||
#include <iss/llvm/jit_helper.h>
|
#include <iss/llvm/jit_helper.h>
|
||||||
@ -78,7 +78,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 memory input file")
|
("backend", po::value<std::string>()->default_value("interp"), "the memory input file")
|
||||||
("isa", po::value<std::string>()->default_value("tgf_c"), "isa to use for simulation");
|
("isa", po::value<std::string>()->default_value("tgc_c"), "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 {
|
||||||
@ -123,24 +123,24 @@ int main(int argc, char *argv[]) {
|
|||||||
iss::vm_ptr vm{nullptr};
|
iss::vm_ptr vm{nullptr};
|
||||||
iss::cpu_ptr cpu{nullptr};
|
iss::cpu_ptr cpu{nullptr};
|
||||||
std::string isa_opt(clim["isa"].as<std::string>());
|
std::string isa_opt(clim["isa"].as<std::string>());
|
||||||
if (isa_opt == "tgf_c") {
|
if (isa_opt == "tgc_c") {
|
||||||
std::tie(cpu, vm) =
|
std::tie(cpu, vm) =
|
||||||
iss::create_cpu<tgc_c_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
|
iss::create_cpu<tgc_c_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
|
||||||
} else
|
} else
|
||||||
#ifdef CORE_TGC_B
|
#ifdef CORE_TGC_B
|
||||||
if (isa_opt == "tgf_b") {
|
if (isa_opt == "tgc_b") {
|
||||||
std::tie(cpu, vm) =
|
std::tie(cpu, vm) =
|
||||||
iss::create_cpu<tgc_b_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
|
iss::create_cpu<tgc_b_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_D
|
#ifdef CORE_TGC_D
|
||||||
if (isa_opt == "tgf_d") {
|
if (isa_opt == "tgc_d") {
|
||||||
std::tie(cpu, vm) =
|
std::tie(cpu, vm) =
|
||||||
iss::create_cpu<tgc_d_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
|
iss::create_cpu<tgc_d_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
LOG(ERROR) << "Illegal argument value for '--isa': " << clim["isa"].as<std::string>() << std::endl;
|
LOG(ERR) << "Illegal argument value for '--isa': " << clim["isa"].as<std::string>() << std::endl;
|
||||||
return 127;
|
return 127;
|
||||||
}
|
}
|
||||||
if (clim.count("plugin")) {
|
if (clim.count("plugin")) {
|
||||||
@ -161,7 +161,7 @@ int main(int argc, char *argv[]) {
|
|||||||
vm->register_plugin(*ce_plugin);
|
vm->register_plugin(*ce_plugin);
|
||||||
plugin_list.push_back(ce_plugin);
|
plugin_list.push_back(ce_plugin);
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Unknown plugin name: " << plugin_name << ", valid names are 'ce', 'ic'" << std::endl;
|
LOG(ERR) << "Unknown plugin name: " << plugin_name << ", valid names are 'ce', 'ic'" << std::endl;
|
||||||
return 127;
|
return 127;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ int main(int argc, char *argv[]) {
|
|||||||
auto cycles = clim["instructions"].as<uint64_t>();
|
auto cycles = clim["instructions"].as<uint64_t>();
|
||||||
res = vm->start(cycles, dump);
|
res = vm->start(cycles, dump);
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
LOG(ERROR) << "Unhandled Exception reached the top of main: " << e.what() << ", application will now exit"
|
LOG(ERR) << "Unhandled Exception reached the top of main: " << e.what() << ", application will now exit"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
res = 2;
|
res = 2;
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,10 @@ iss::plugin::cycle_estimate::cycle_estimate(std::string config_file_name)
|
|||||||
try {
|
try {
|
||||||
is >> root;
|
is >> root;
|
||||||
} catch (Json::RuntimeError &e) {
|
} catch (Json::RuntimeError &e) {
|
||||||
LOG(ERROR) << "Could not parse input file " << config_file_name << ", reason: " << e.what();
|
LOG(ERR) << "Could not parse input file " << config_file_name << ", reason: " << e.what();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Could not open input file " << config_file_name;
|
LOG(ERR) << "Could not open input file " << config_file_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR)<<"plugin cycle_estimate: could not find an entry for "<<core_name<<" in JSON file"<<std::endl;
|
LOG(ERR)<<"plugin cycle_estimate: could not find an entry for "<<core_name<<" in JSON file"<<std::endl;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -87,6 +87,8 @@ void iss::plugin::cycle_estimate::callback(instr_info_t instr_info, exec_info co
|
|||||||
assert(arch_instr && "No instrumentation interface available but callback executed");
|
assert(arch_instr && "No instrumentation interface available but callback executed");
|
||||||
auto entry = delays[instr_info.instr_id];
|
auto entry = delays[instr_info.instr_id];
|
||||||
bool taken = (arch_instr->get_next_pc()-arch_instr->get_pc()) != (entry.size/8);
|
bool taken = (arch_instr->get_next_pc()-arch_instr->get_pc()) != (entry.size/8);
|
||||||
uint32_t delay = taken ? entry.taken : entry.not_taken;
|
if (taken && entry.taken > 1)
|
||||||
if(delay>1) arch_instr->set_curr_instr_cycles(delay);
|
arch_instr->set_curr_instr_cycles(entry.taken);
|
||||||
|
else if (entry.not_taken > 1)
|
||||||
|
arch_instr->set_curr_instr_cycles(entry.not_taken);
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,10 @@ iss::plugin::instruction_count::instruction_count(std::string config_file_name)
|
|||||||
try {
|
try {
|
||||||
is >> root;
|
is >> root;
|
||||||
} catch (Json::RuntimeError &e) {
|
} catch (Json::RuntimeError &e) {
|
||||||
LOG(ERROR) << "Could not parse input file " << config_file_name << ", reason: " << e.what();
|
LOG(ERR) << "Could not parse input file " << config_file_name << ", reason: " << e.what();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Could not open input file " << config_file_name;
|
LOG(ERR) << "Could not open input file " << config_file_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ bool iss::plugin::instruction_count::registration(const char* const version, vm_
|
|||||||
}
|
}
|
||||||
rep_counts.resize(delays.size());
|
rep_counts.resize(delays.size());
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR)<<"plugin instruction_count: could not find an entry for "<<core_name<<" in JSON file"<<std::endl;
|
LOG(ERR)<<"plugin instruction_count: could not find an entry for "<<core_name<<" in JSON file"<<std::endl;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -30,39 +30,54 @@
|
|||||||
*
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#include "iss/debugger/gdb_session.h"
|
||||||
|
#include "iss/debugger/encoderdecoder.h"
|
||||||
|
#include "iss/debugger/server.h"
|
||||||
|
#include "iss/debugger/target_adapter_if.h"
|
||||||
|
#include "iss/iss.h"
|
||||||
|
#include "iss/vm_types.h"
|
||||||
#include "sysc/core_complex.h"
|
#include "sysc/core_complex.h"
|
||||||
#ifdef CORE_TGC_B
|
#ifdef CORE_TGC_B
|
||||||
#include "iss/arch/riscv_hart_m_p.h"
|
#include "iss/arch/riscv_hart_m_p.h"
|
||||||
#include "iss/arch/tgc_b.h"
|
#include "iss/arch/tgc_b.h"
|
||||||
using tgc_b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_b>;
|
using tgc_b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_b>;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_C
|
|
||||||
#include "iss/arch/riscv_hart_m_p.h"
|
#include "iss/arch/riscv_hart_m_p.h"
|
||||||
#include "iss/arch/tgc_c.h"
|
#include "iss/arch/tgc_c.h"
|
||||||
using tgc_c_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_c>;
|
using tgc_c_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_c>;
|
||||||
#endif
|
|
||||||
#ifdef CORE_TGC_D
|
#ifdef CORE_TGC_D
|
||||||
#include "iss/arch/riscv_hart_mu_p.h"
|
#include "iss/arch/riscv_hart_mu_p.h"
|
||||||
#include "iss/arch/tgc_d.h"
|
#include "iss/arch/tgc_d.h"
|
||||||
using tgc_d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d, iss::arch::FEAT_PMP>;
|
using tgc_d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d, iss::arch::FEAT_PMP>;
|
||||||
#endif
|
#endif
|
||||||
#include "iss/debugger/encoderdecoder.h"
|
|
||||||
#include "iss/debugger/gdb_session.h"
|
|
||||||
#include "iss/debugger/server.h"
|
|
||||||
#include "iss/debugger/target_adapter_if.h"
|
|
||||||
#include "iss/iss.h"
|
|
||||||
#include "iss/vm_types.h"
|
|
||||||
#include "scc/report.h"
|
#include "scc/report.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <array>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
#define STR(X) #X
|
#define STR(X) #X
|
||||||
#define CREATE_CORE(CN) \
|
#define CREATE_CORE(CN) \
|
||||||
if (type == STR(CN)) { std::tie(cpu, vm) = create_core<CN ## _plat_type>(backend, gdb_port, hart_id); } else
|
if (type == STR(CN)) { std::tie(cpu, vm) = create_core<CN ## _plat_type>(backend, gdb_port, hart_id); } else
|
||||||
|
|
||||||
#ifdef WITH_SCV
|
#ifdef WITH_SCV
|
||||||
#include <array>
|
|
||||||
#include <scv.h>
|
#include <scv.h>
|
||||||
|
#else
|
||||||
|
#include <scv-tr.h>
|
||||||
|
using namespace scv_tr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CWR_SYSTEMC
|
||||||
|
#define GET_PROP_VALUE(P) P.get_value()
|
||||||
|
#else
|
||||||
|
#define GET_PROP_VALUE(P) P.getValue()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
// not #if defined(_WIN32) || defined(_WIN64) because we have strncasecmp in mingw
|
||||||
|
#define strncasecmp _strnicmp
|
||||||
|
#define strcasecmp _stricmp
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace sysc {
|
namespace sysc {
|
||||||
@ -81,8 +96,8 @@ std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
|||||||
template<typename PLAT>
|
template<typename PLAT>
|
||||||
class core_wrapper_t : public PLAT {
|
class core_wrapper_t : public PLAT {
|
||||||
public:
|
public:
|
||||||
using reg_t = typename arch::traits<typename PLAT::super>::reg_t;
|
using reg_t = typename arch::traits<typename PLAT::core>::reg_t;
|
||||||
using phys_addr_t = typename arch::traits<typename PLAT::super>::phys_addr_t;
|
using phys_addr_t = typename arch::traits<typename PLAT::core>::phys_addr_t;
|
||||||
using heart_state_t = typename PLAT::hart_state_type;
|
using heart_state_t = typename PLAT::hart_state_type;
|
||||||
core_wrapper_t(core_complex *owner)
|
core_wrapper_t(core_complex *owner)
|
||||||
: owner(owner) { }
|
: owner(owner) { }
|
||||||
@ -102,15 +117,14 @@ public:
|
|||||||
sync_type needed_sync() const override { return PRE_SYNC; }
|
sync_type needed_sync() const override { return PRE_SYNC; }
|
||||||
|
|
||||||
void disass_output(uint64_t pc, const std::string instr) override {
|
void disass_output(uint64_t pc, const std::string instr) override {
|
||||||
if (INFO <= Log<Output2FILE<disass>>::reporting_level() && Output2FILE<disass>::stream()) {
|
if (!owner->disass_output(pc, instr)) {
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
s << "[p:" << lvl[this->reg.PRIV] << ";s:0x" << std::hex << std::setfill('0')
|
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 << "]";
|
<< std::setw(sizeof(reg_t) * 2) << (reg_t)this->state.mstatus << std::dec << ";c:" << this->reg.icount << "]";
|
||||||
Log<Output2FILE<disass>>().get(INFO, "disass")
|
SCCDEBUG(owner->name())<<"disass: "
|
||||||
<< "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40)
|
<< "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40)
|
||||||
<< std::setfill(' ') << std::left << instr << s.str();
|
<< std::setfill(' ') << std::left << instr << s.str();
|
||||||
}
|
}
|
||||||
owner->disass_output(pc, instr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data) override {
|
status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data) override {
|
||||||
@ -137,6 +151,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
status read_csr(unsigned addr, reg_t &val) override {
|
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)){
|
if((addr==arch::time || addr==arch::timeh) && owner->mtime_o.get_interface(0)){
|
||||||
uint64_t time_val;
|
uint64_t time_val;
|
||||||
bool ret = owner->mtime_o->nb_peek(time_val);
|
bool ret = owner->mtime_o->nb_peek(time_val);
|
||||||
@ -147,6 +162,17 @@ public:
|
|||||||
val = static_cast<reg_t>(time_val >> 32);
|
val = static_cast<reg_t>(time_val >> 32);
|
||||||
}
|
}
|
||||||
return ret?Ok:Err;
|
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 {
|
} else {
|
||||||
return PLAT::read_csr(addr, val);
|
return PLAT::read_csr(addr, val);
|
||||||
}
|
}
|
||||||
@ -155,7 +181,7 @@ public:
|
|||||||
void wait_until(uint64_t flags) override {
|
void wait_until(uint64_t flags) override {
|
||||||
SCCDEBUG(owner->name()) << "Sleeping until interrupt";
|
SCCDEBUG(owner->name()) << "Sleeping until interrupt";
|
||||||
do {
|
do {
|
||||||
wait(wfi_evt);
|
sc_core::wait(wfi_evt);
|
||||||
} while (this->reg.pending_trap == 0);
|
} while (this->reg.pending_trap == 0);
|
||||||
PLAT::wait_until(flags);
|
PLAT::wait_until(flags);
|
||||||
}
|
}
|
||||||
@ -248,7 +274,7 @@ public:
|
|||||||
set_interrupt_execution = [lcpu](bool b) { return lcpu->set_interrupt_execution(b); };
|
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); };
|
local_irq = [lcpu](short s, bool b) { return lcpu->local_irq(s, b); };
|
||||||
if(backend == "interp")
|
if(backend == "interp")
|
||||||
return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(lcpu, gdb_port)}};
|
return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(static_cast<typename PLAT::core*>(lcpu), gdb_port)}};
|
||||||
#ifdef WITH_LLVM
|
#ifdef WITH_LLVM
|
||||||
if(backend == "llvm")
|
if(backend == "llvm")
|
||||||
return {cpu_ptr{lcpu}, vm_ptr{iss::llvm::create(lcpu, gdb_port)}};
|
return {cpu_ptr{lcpu}, vm_ptr{iss::llvm::create(lcpu, gdb_port)}};
|
||||||
@ -263,13 +289,13 @@ public:
|
|||||||
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)
|
CREATE_CORE(tgc_c)
|
||||||
#ifdef CORE_TGC_B
|
#ifdef CORE_TGC_B
|
||||||
CREATE_CORE(tgc_c)
|
CREATE_CORE(tgc_b)
|
||||||
#endif
|
#endif
|
||||||
#ifdef CORE_TGC_D
|
#ifdef CORE_TGC_D
|
||||||
CREATE_CORE(tgc_d)
|
CREATE_CORE(tgc_d)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
LOG(ERROR) << "Illegal argument value for core type: " << type << std::endl;
|
LOG(ERR) << "Illegal argument value for core type: " << type << std::endl;
|
||||||
}
|
}
|
||||||
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();
|
||||||
@ -287,18 +313,29 @@ public:
|
|||||||
iss::debugger::target_adapter_if *tgt_adapter{nullptr};
|
iss::debugger::target_adapter_if *tgt_adapter{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
core_complex::core_complex(sc_module_name name)
|
struct core_trace {
|
||||||
|
//! transaction recording database
|
||||||
|
scv_tr_db *m_db{nullptr};
|
||||||
|
//! blocking transaction recording stream handle
|
||||||
|
scv_tr_stream *stream_handle{nullptr};
|
||||||
|
//! transaction generator handle for blocking transactions
|
||||||
|
scv_tr_generator<_scv_tr_generator_default_data, _scv_tr_generator_default_data> *instr_tr_handle{nullptr};
|
||||||
|
scv_tr_handle tr_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
SC_HAS_PROCESS(core_complex);// NOLINT
|
||||||
|
#ifndef CWR_SYSTEMC
|
||||||
|
core_complex::core_complex(sc_module_name const& name)
|
||||||
: sc_module(name)
|
: sc_module(name)
|
||||||
, read_lut(tlm_dmi_ext())
|
, read_lut(tlm_dmi_ext())
|
||||||
, write_lut(tlm_dmi_ext())
|
, write_lut(tlm_dmi_ext())
|
||||||
#ifdef WITH_SCV
|
|
||||||
, m_db(scv_tr_db::get_default_db())
|
|
||||||
, stream_handle(nullptr)
|
|
||||||
, instr_tr_handle(nullptr)
|
|
||||||
, fetch_tr_handle(nullptr)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
SC_HAS_PROCESS(core_complex);// NOLINT
|
init();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void core_complex::init(){
|
||||||
|
trc=new core_trace();
|
||||||
initiator.register_invalidate_direct_mem_ptr([=](uint64_t start, uint64_t end) -> void {
|
initiator.register_invalidate_direct_mem_ptr([=](uint64_t start, uint64_t end) -> void {
|
||||||
auto lut_entry = read_lut.getEntry(start);
|
auto lut_entry = read_lut.getEntry(start);
|
||||||
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE && end <= lut_entry.get_end_address() + 1) {
|
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE && end <= lut_entry.get_end_address() + 1) {
|
||||||
@ -311,8 +348,6 @@ core_complex::core_complex(sc_module_name name)
|
|||||||
});
|
});
|
||||||
|
|
||||||
SC_THREAD(run);
|
SC_THREAD(run);
|
||||||
SC_METHOD(clk_cb);
|
|
||||||
sensitive << clk_i;
|
|
||||||
SC_METHOD(rst_cb);
|
SC_METHOD(rst_cb);
|
||||||
sensitive << rst_i;
|
sensitive << rst_i;
|
||||||
SC_METHOD(sw_irq_cb);
|
SC_METHOD(sw_irq_cb);
|
||||||
@ -321,61 +356,82 @@ core_complex::core_complex(sc_module_name name)
|
|||||||
sensitive << timer_irq_i;
|
sensitive << timer_irq_i;
|
||||||
SC_METHOD(global_irq_cb);
|
SC_METHOD(global_irq_cb);
|
||||||
sensitive << global_irq_i;
|
sensitive << global_irq_i;
|
||||||
|
trc->m_db=scv_tr_db::get_default_db();
|
||||||
|
|
||||||
|
SC_METHOD(forward);
|
||||||
|
#ifndef CWR_SYSTEMC
|
||||||
|
sensitive<<clk_i;
|
||||||
|
#else
|
||||||
|
sensitive<<curr_clk;
|
||||||
|
t2t.reset(new scc::tick2time{"t2t"});
|
||||||
|
t2t->clk_i(clk_i);
|
||||||
|
t2t->clk_o(curr_clk);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
core_complex::~core_complex() = default;
|
core_complex::~core_complex(){
|
||||||
|
delete cpu;
|
||||||
|
delete trc;
|
||||||
|
}
|
||||||
|
|
||||||
void core_complex::trace(sc_trace_file *trf) const {}
|
void core_complex::trace(sc_trace_file *trf) const {}
|
||||||
|
|
||||||
void core_complex::before_end_of_elaboration() {
|
void core_complex::before_end_of_elaboration() {
|
||||||
SCCDEBUG(SCMOD)<<"instantiating iss::arch::tgf with "<<backend.get_value()<<" backend";
|
SCCDEBUG(SCMOD)<<"instantiating iss::arch::tgf with "<<GET_PROP_VALUE(backend)<<" backend";
|
||||||
cpu = scc::make_unique<core_wrapper>(this);
|
// cpu = scc::make_unique<core_wrapper>(this);
|
||||||
cpu->create_cpu(core_type.get_value(), backend.get_value(), gdb_server_port.get_value(), mhartid.get_value());
|
cpu = new core_wrapper(this);
|
||||||
|
cpu->create_cpu(GET_PROP_VALUE(core_type), GET_PROP_VALUE(backend), GET_PROP_VALUE(gdb_server_port), GET_PROP_VALUE(mhartid));
|
||||||
sc_assert(cpu->vm!=nullptr);
|
sc_assert(cpu->vm!=nullptr);
|
||||||
#ifdef WITH_SCV
|
cpu->vm->setDisassEnabled(GET_PROP_VALUE(enable_disass) || trc->m_db != nullptr);
|
||||||
cpu->vm->setDisassEnabled(enable_disass.get_value() || m_db != nullptr);
|
|
||||||
#else
|
|
||||||
vm->setDisassEnabled(enable_disass.get_value());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void core_complex::start_of_simulation() {
|
void core_complex::start_of_simulation() {
|
||||||
quantum_keeper.reset();
|
quantum_keeper.reset();
|
||||||
if (elf_file.get_value().size() > 0) {
|
if (GET_PROP_VALUE(elf_file).size() > 0) {
|
||||||
istringstream is(elf_file.get_value());
|
istringstream is(GET_PROP_VALUE(elf_file));
|
||||||
string s;
|
string s;
|
||||||
while (getline(is, s, ',')) {
|
while (getline(is, s, ',')) {
|
||||||
std::pair<uint64_t, bool> start_addr = cpu->load_file(s);
|
std::pair<uint64_t, bool> start_addr = cpu->load_file(s);
|
||||||
|
#ifndef CWR_SYSTEMC
|
||||||
if (reset_address.is_default_value() && start_addr.second == true)
|
if (reset_address.is_default_value() && start_addr.second == true)
|
||||||
reset_address.set_value(start_addr.first);
|
reset_address.set_value(start_addr.first);
|
||||||
|
#else
|
||||||
|
if (start_addr.second == true)
|
||||||
|
reset_address=start_addr.first;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef WITH_SCV
|
if (trc->m_db != nullptr && trc->stream_handle == nullptr) {
|
||||||
if (m_db != nullptr && stream_handle == nullptr) {
|
|
||||||
string basename(this->name());
|
string basename(this->name());
|
||||||
stream_handle = new scv_tr_stream((basename + ".instr").c_str(), "TRANSACTOR", m_db);
|
trc->stream_handle = new scv_tr_stream((basename + ".instr").c_str(), "TRANSACTOR", trc->m_db);
|
||||||
instr_tr_handle = new scv_tr_generator<>("execute", *stream_handle);
|
trc->instr_tr_handle = new scv_tr_generator<>("execute", *trc->stream_handle);
|
||||||
fetch_tr_handle = new scv_tr_generator<uint64_t>("fetch", *stream_handle);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool core_complex::disass_output(uint64_t pc, const std::string instr_str) {
|
||||||
|
if (trc->m_db == nullptr) return false;
|
||||||
|
if (trc->tr_handle.is_active()) trc->tr_handle.end_transaction();
|
||||||
|
trc->tr_handle = trc->instr_tr_handle->begin_transaction();
|
||||||
|
trc->tr_handle.record_attribute("PC", pc);
|
||||||
|
trc->tr_handle.record_attribute("INSTR", instr_str);
|
||||||
|
trc->tr_handle.record_attribute("MODE", lvl[cpu->get_mode()]);
|
||||||
|
trc->tr_handle.record_attribute("MSTATUS", cpu->get_state());
|
||||||
|
trc->tr_handle.record_attribute("LTIME_START", quantum_keeper.get_current_time().value() / 1000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void core_complex::forward() {
|
||||||
|
#ifndef CWR_SYSTEMC
|
||||||
|
set_clock_period(clk_i.read());
|
||||||
|
#else
|
||||||
|
set_clock_period(curr_clk.read());
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void core_complex::disass_output(uint64_t pc, const std::string instr_str) {
|
void core_complex::set_clock_period(sc_core::sc_time period) {
|
||||||
#ifdef WITH_SCV
|
curr_clk = period;
|
||||||
if (m_db == nullptr) return;
|
if (period == SC_ZERO_TIME) cpu->set_interrupt_execution(true);
|
||||||
if (tr_handle.is_active()) tr_handle.end_transaction();
|
|
||||||
tr_handle = instr_tr_handle->begin_transaction();
|
|
||||||
tr_handle.record_attribute("PC", pc);
|
|
||||||
tr_handle.record_attribute("INSTR", instr_str);
|
|
||||||
tr_handle.record_attribute("MODE", lvl[cpu->get_mode()]);
|
|
||||||
tr_handle.record_attribute("MSTATUS", cpu->get_state());
|
|
||||||
tr_handle.record_attribute("LTIME_START", quantum_keeper.get_current_time().value() / 1000);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void core_complex::clk_cb() {
|
|
||||||
curr_clk = clk_i.read();
|
|
||||||
if (curr_clk == SC_ZERO_TIME) cpu->set_interrupt_execution(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void core_complex::rst_cb() {
|
void core_complex::rst_cb() {
|
||||||
@ -392,11 +448,11 @@ void core_complex::run() {
|
|||||||
wait(SC_ZERO_TIME); // separate from elaboration phase
|
wait(SC_ZERO_TIME); // separate from elaboration phase
|
||||||
do {
|
do {
|
||||||
if (rst_i.read()) {
|
if (rst_i.read()) {
|
||||||
cpu->reset(reset_address.get_value());
|
cpu->reset(GET_PROP_VALUE(reset_address));
|
||||||
wait(rst_i.negedge_event());
|
wait(rst_i.negedge_event());
|
||||||
}
|
}
|
||||||
while (clk_i.read() == SC_ZERO_TIME) {
|
while (curr_clk.read() == SC_ZERO_TIME) {
|
||||||
wait(clk_i.value_changed_event());
|
wait(curr_clk.value_changed_event());
|
||||||
}
|
}
|
||||||
cpu->set_interrupt_execution(false);
|
cpu->set_interrupt_execution(false);
|
||||||
cpu->start();
|
cpu->start();
|
||||||
@ -420,15 +476,13 @@ bool core_complex::read_mem(uint64_t addr, unsigned length, uint8_t *const data,
|
|||||||
gp.set_data_length(length);
|
gp.set_data_length(length);
|
||||||
gp.set_streaming_width(length);
|
gp.set_streaming_width(length);
|
||||||
sc_time delay=quantum_keeper.get_local_time();
|
sc_time delay=quantum_keeper.get_local_time();
|
||||||
#ifdef WITH_SCV
|
if (trc->m_db != nullptr && trc->tr_handle.is_valid()) {
|
||||||
if (m_db != nullptr && tr_handle.is_valid()) {
|
if (is_fetch && trc->tr_handle.is_active()) {
|
||||||
if (is_fetch && tr_handle.is_active()) {
|
trc->tr_handle.end_transaction();
|
||||||
tr_handle.end_transaction();
|
|
||||||
}
|
}
|
||||||
auto preExt = new tlm::scc::scv::tlm_recording_extension(tr_handle, this);
|
auto preExt = new tlm::scc::scv::tlm_recording_extension(trc->tr_handle, this);
|
||||||
gp.set_extension(preExt);
|
gp.set_extension(preExt);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
initiator->b_transport(gp, delay);
|
initiator->b_transport(gp, delay);
|
||||||
SCCTRACE(this->name()) << "read_mem(0x" << std::hex << addr << ") : " << data;
|
SCCTRACE(this->name()) << "read_mem(0x" << std::hex << addr << ") : " << data;
|
||||||
if (gp.get_response_status() != tlm::TLM_OK_RESPONSE) {
|
if (gp.get_response_status() != tlm::TLM_OK_RESPONSE) {
|
||||||
@ -469,12 +523,10 @@ bool core_complex::write_mem(uint64_t addr, unsigned length, const uint8_t *cons
|
|||||||
gp.set_data_length(length);
|
gp.set_data_length(length);
|
||||||
gp.set_streaming_width(length);
|
gp.set_streaming_width(length);
|
||||||
sc_time delay=quantum_keeper.get_local_time();
|
sc_time delay=quantum_keeper.get_local_time();
|
||||||
#ifdef WITH_SCV
|
if (trc->m_db != nullptr && trc->tr_handle.is_valid()) {
|
||||||
if (m_db != nullptr && tr_handle.is_valid()) {
|
auto preExt = new tlm::scc::scv::tlm_recording_extension(trc->tr_handle, this);
|
||||||
auto preExt = new tlm::scc::scv::tlm_recording_extension(tr_handle, this);
|
|
||||||
gp.set_extension(preExt);
|
gp.set_extension(preExt);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
initiator->b_transport(gp, delay);
|
initiator->b_transport(gp, delay);
|
||||||
quantum_keeper.set(delay);
|
quantum_keeper.set(delay);
|
||||||
SCCTRACE() << "write_mem(0x" << std::hex << addr << ") : " << data;
|
SCCTRACE() << "write_mem(0x" << std::hex << addr << ") : " << data;
|
||||||
@ -537,6 +589,5 @@ bool core_complex::write_mem_dbg(uint64_t addr, unsigned length, const uint8_t *
|
|||||||
return initiator->transport_dbg(gp) == length;
|
return initiator->transport_dbg(gp) == length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace SiFive */
|
} /* namespace SiFive */
|
||||||
} /* namespace sysc */
|
} /* namespace sysc */
|
||||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user