179 Commits

Author SHA1 Message Date
c792f50427 Merge branch 'develop' of https://git.minres.com/DBT-RISE/DBT-RISE-TGC into develop 2023-05-16 21:57:32 +02:00
6ed7eafc5d adds inital version of tcc backend 2023-05-16 21:51:35 +02:00
8a5fe58d51 adds needed arch state members for TCC to tgc_c 2023-05-16 08:56:18 +02:00
16cd6d5ff5 fixes core name deduction in cmake build script 2023-05-16 08:54:08 +02:00
ee2ded931d adds remaining register offsets 2023-05-14 17:16:42 +02:00
95ba5c901a re-introduces last_branch register 2023-05-14 17:00:37 +02:00
32848ec396 fixes build system and typo in wt_cache 2023-05-13 16:57:01 +02:00
6789cf4c32 fixes case of unavailable backend 2023-05-12 15:45:53 +02:00
3bc4884a9d remove unneeded cmake include 2023-05-12 09:28:43 +02:00
fd6b738168 changes compile dependencies 2023-05-11 23:43:12 +02:00
afdf8fb97f adds missing namespaces 2023-05-11 23:11:04 +02:00
cfa7b72363 changes time handling at sockets 2023-05-06 19:57:29 +02:00
d330307ed5 splits bus into 2 sockets for i/dbus 2023-05-04 21:59:31 +02:00
916de2a26d changes build setup to compile specific files if a core is specified 2023-05-04 16:08:33 +02:00
aa70d8a54a fixes CLIC to match clicinfo description in CLIC spec 11.04.2023 2023-05-02 17:22:13 +02:00
b493745cd7 sets reset start time to 0 2023-05-02 11:21:42 +02:00
f9e8e1d857 fixes core_complex wrt. tlm quantum and DMI 2023-05-02 11:13:25 +02:00
974d64a627 adds logo to imported instance 2023-05-02 08:17:17 +02:00
d70489cbb8 update import script to initialize broker 2023-05-02 07:58:48 +02:00
d990f1cf5d fixes reading of 64bit CSR register 2023-05-01 22:23:35 +02:00
1672b01e62 adds WT cache functionality as mixin 2023-04-28 20:38:07 +02:00
00b0f101ac adapts to changes of instrumentation interface in dbt-rise-core 2023-04-28 20:38:07 +02:00
54f75f92ea improved testbench import; added prebuild FW for testing 2023-04-24 08:44:12 -07:00
0304aac9e5 fixed some issues in import script; added README for reference; added initial testbench script(to be improved) 2023-04-19 05:20:58 -07:00
8ff55d7b92 updates CWR dependent core_complex definition 2023-04-14 19:34:41 +02:00
f626ee2684 fixes privilege wrapper for M/U to cope with 64bit 2023-04-05 15:38:25 +02:00
a8a2782329 adds changes from latest CoreDSL description 2023-04-04 16:10:12 +02:00
98dd329833 fixes CSR access rights 2023-04-04 09:23:08 +02:00
6213445bc4 fixes 64bit behavior of CSR regs 2023-03-27 12:04:43 +02:00
c5465bf9e2 fixes according to fixed generator 2023-03-26 14:44:15 +02:00
d881cb6e63 fix data width of generated code 2023-03-26 12:12:34 +02:00
2e4faa4d50 fixes mstatus mask 2023-03-25 09:14:56 +01:00
8e1951f298 adds 64bit mstatus 2023-03-23 07:47:21 +01:00
7efa924510 fixes m/uintstatus read 2023-03-17 10:51:39 +01:00
febbc4fff0 fixes m/uintstatus read 2023-03-17 10:23:05 +01:00
39b2788b7e implements and fixes CLIC CSR behavior 2023-03-17 09:09:09 +01:00
a943dd3bdf fixes wrong array size which led to unintended CSR definitions 2023-03-15 14:16:08 +01:00
fedbff5971 fixes xcause and u-mode clic CSRs 2023-03-15 12:27:39 +01:00
c2758e8321 removes mscratchcsw from CLIC feature 2023-03-15 09:07:00 +01:00
8be5fe71df fixes template name typo 2023-03-12 07:42:09 +01:00
3f7ce41b9d fixes CLIC mtvt register behavior 2023-03-11 14:03:03 +01:00
ad1cbedf00 adds back missing max irq functions 2023-03-11 12:47:10 +01:00
83f54b5074 fixes CLICCFG settings 2023-03-11 08:48:03 +01:00
a83928fd8c fixes CSR/CLIC implementation 2023-03-10 20:40:21 +01:00
ec55efd322 adds generator changed files 2023-02-17 06:36:34 +01:00
8c3709f92a adds generator changed files 2023-02-17 06:29:27 +01:00
207dbf1071 fixes out of range access for register alias names 2023-02-17 06:28:30 +01:00
62c118e501 fixes CSR to match latest fast interrupts spec 2023-01-20 16:21:04 +01:00
65dca13b42 fixes WFI miss of interrupt 2023-01-14 17:40:21 +01:00
3187cbdfe2 removes CONAN_PKG from build system 2022-12-12 02:55:44 +01:00
8c701d55c1 adapt to latest changes in SCC 2022-12-05 09:15:48 +01:00
f585489ff5 fixes pin naming 2022-10-26 17:21:44 +02:00
7113683ee0 moves pending interrupt check before handling trap thus saving 1 cycle 2022-10-15 10:47:35 +02:00
1a0fc4bd5d fixes wrong mcounteren in M-mode only priv wrapper 2022-10-10 08:59:27 +02:00
40d1966e9a fixes pending irq within irq hander behavior 2022-10-08 11:20:52 +02:00
a977200284 cleans up priv wrappers 2022-10-05 08:58:57 +02:00
b20fd3eba5 fix static build 2022-09-28 19:37:47 +02:00
b20daa1ac2 fixes wrong path in install 2022-09-27 09:11:41 +02:00
b1a18459e7 adds more flexible use of availabel targets 2022-09-26 13:57:24 +02:00
6ba7c82f80 fixes wrapper definitions for hwl cores 2022-09-26 13:31:46 +02:00
ad7bb28b4c fixes write mask of clic memory mapped registers 2022-09-17 12:15:19 +02:00
fa7eda0889 fixes wrong check for exception 2022-08-31 11:45:53 +02:00
00e02bf565 adds support for different branch types in tracing 2022-08-08 06:30:37 +02:00
1ad66a71d8 extends supported break point types 2022-08-06 09:53:24 +02:00
e60fa3d5e6 adaptes to changes in dbt-rise-core 2022-08-06 09:49:32 +02:00
8407f6287f replaces core_complex socket 2022-07-24 20:52:28 +02:00
0833198d34 aads missing windows compat firx to template 2022-07-23 14:36:23 +02:00
57347ae4d9 fixes cppcheck flagged issues 2022-07-23 13:49:10 +02:00
4876f18ba9 adds windows compatibility fixes 2022-07-18 11:43:42 +02:00
a53ee42e13 updates TGC_C according to CoreDSL description update 2022-07-12 22:34:22 +02:00
12ccfc055a updates generate tgc_c definition 2022-07-11 22:58:10 +02:00
feaa49d367 removes decoder again as there is some issue 2022-06-20 00:39:11 +02:00
18f33b4a68 fixes ordering of instructions for decoding 2022-06-19 16:52:29 +02:00
f096b15dbd factors decoder into separate component 2022-06-19 13:17:31 +02:00
cb5375258a removes compilatioon of unneeded files 2022-06-10 07:19:46 +02:00
076b5a39ad fix class naming 2022-06-02 08:30:49 +02:00
f40ab41899 fix left-over from layout refactoring 2022-06-02 08:30:02 +02:00
e8fd5143bc fix build options for standalone ISS 2022-05-31 11:05:26 +02:00
31fb51de95 update tgc_c generated code 2022-05-30 22:15:44 +02:00
5d481eb79d fix generation of non-exception code 2022-05-30 22:04:16 +02:00
1c90fe765d Merge remote-tracking branch 'origin/Trace_enhancement' into develop 2022-05-30 14:18:09 +02:00
52ed8b81a6 fixed template to work with previous code generator 2022-05-30 14:08:02 +02:00
0703a0a845 update tgc-mapper 2022-05-30 07:45:32 +02:00
0c542d42aa separate generated sources 2022-05-21 12:48:28 +02:00
966d1616c5 change source code to unified layout 2022-05-21 11:55:24 +02:00
1720bd4aaa adds support for compressed instructions 2022-05-20 15:17:58 +02:00
df16378605 update template for changed code generator 2022-05-18 19:10:34 +02:00
1438f0f373 add backannotation to pc trace plugin 2022-05-17 15:29:04 +02:00
766f3ba9ee fix assertion in compressed pctrace writer 2022-05-13 12:38:12 +02:00
5da4e6b424 fix alignment check for unaligned debugger accesses 2022-05-13 12:37:47 +02:00
e382217e04 update vm_tgc_c due reworked CoreDSL generator 2022-05-11 18:52:15 +02:00
9db4e3fd87 fix assertion 2022-05-10 16:13:21 +02:00
bb658be3b4 Merge branch 'develop' of https://git.minres.com/DBT-RISE/DBT-RISE-TGC into develop 2022-05-08 15:25:56 +02:00
6579780dc9 add call column in output 2022-05-08 15:24:26 +02:00
e56bc12788 fix non-lz4 build of plugin 2022-05-07 17:27:11 +02:00
e88f309ea2 add lz4 compression to pctrace 2022-05-07 17:22:06 +02:00
03bec27376 implement extended instrumentation interface 2022-04-26 17:14:33 +02:00
9d9008a3a2 fix pointer mess 2022-04-26 15:35:17 +02:00
5f6d462973 check that no interrupts are pending before entering the wfi wait 2022-04-26 13:58:20 +02:00
a92b84bef4 add code word access for ISS plugins 2022-04-25 14:18:19 +02:00
477c530847 extend debug mode handling 2022-04-13 11:41:01 +02:00
c054d75717 update to latest coredsl description 2022-04-10 18:55:44 +02:00
15cd26f800 remove CoreDSL ISA repo 2022-04-10 12:15:40 +02:00
9465cffe79 adapt to change in dbt-rise-core 2022-04-09 14:55:36 +02:00
00d2d06cbd adapt to privileged spec 2022-03-31 20:33:12 +02:00
8e4e702cb9 Merge remote-tracking branch 'origin/feature/reduced_output' into develop 2022-03-28 14:09:06 +02:00
58311b37db Merge branch 'feature/reduced_output' of
https://git.minres.com/DBT-RISE/DBT-RISE-TGC.git into
feature/reduced_output
2022-03-28 11:16:09 +02:00
ad8dc09bee Merge branch 'feature/reduced_output' of https://git.minres.com/DBT-RISE/DBT-RISE-TGC.git into feature/reduced_output 2022-03-28 11:15:45 +02:00
49be143588 make features configurable 2022-03-27 17:54:08 +02:00
0aea1d0177 remove mcounteren in M-mode only wrapper 2022-03-27 17:21:46 +02:00
6ea7721961 add TCM 2022-03-27 15:38:18 +02:00
b0cb997009 add TGC_X with DMR 2022-03-26 10:48:21 +01:00
9dfca612b7 add hardware loop CSR access 2022-03-25 11:33:44 +01:00
30ae743361 add pctrace plugin to iss 2022-03-20 17:41:54 +01:00
d91f5f9df4 fix compiler warning for reduced number of registers 2022-03-14 15:38:05 +01:00
5ec457c76b build pctrace plugin only if RapidJSON target is availble 2022-03-08 11:23:07 +01:00
2e670c4d03 change interpreter structure 2022-03-06 15:11:38 +01:00
3d32c33333 update gitignore 2022-03-05 20:59:45 +01:00
521f40a3d6 refactored interpreter backend structure 2022-03-05 20:59:17 +01:00
2bba5645c3 adds functionality to reduce the output 2022-02-16 10:13:29 +01:00
bf0a5a80de adds functionality to reduce the output 2022-02-16 10:12:45 +01:00
b37ef973de clean up 2022-02-14 20:36:12 +01:00
4c363f4073 adds additional functionality by fetching delay information 2022-02-11 11:28:00 +01:00
b8fa5fbbda adapt to extended instrumentation interface 2022-02-09 21:01:17 +01:00
ac86f14a54 add tgc_c_xrb_nn to tgc-sim 2022-02-02 21:33:42 +01:00
68b5697c8f Fix cycles JSON template 2022-02-01 21:48:56 +01:00
09b0f0d0c8 fix cycle estimation plugin 2022-02-01 21:14:50 +01:00
98b418ff43 fix JSON reading 2022-02-01 19:28:11 +01:00
059bd0d371 rework cycle estimation 2022-02-01 19:03:45 +01:00
ef2a4df925 simplify spawn block handling 2022-01-31 23:40:31 +01:00
7578906310 adds coverage plugin 2022-01-31 21:38:18 +01:00
afe8905ac9 fix else-ambiguity in CoreDSL description 2022-01-31 20:30:46 +01:00
ecc6091d1e cleans up source code to remove clang compiler warnings 2022-01-19 08:01:15 +01:00
3563ba80d0 add spawn blocks 2022-01-12 07:21:16 +01:00
09955be90f update gitignore 2021-12-05 08:45:49 +01:00
dd4c19a15c add option to configure number of irq 2021-12-01 12:56:36 +01:00
07d5af1dde fix stand-alone ISS compilation to include all generated cores 2021-11-26 17:56:40 +01:00
6f8595759e make tgc-sim include all available ISS 2021-11-25 20:00:27 +01:00
86da31033c correct size usage in pmp addr checks 2021-11-22 15:15:47 +01:00
974d103381 fix pmpcfg register write 2021-11-22 10:49:29 +01:00
309758b994 fix clic_cfg access scheme 2021-11-17 07:59:02 +01:00
965929d1eb remove descriptions 2021-11-15 09:30:16 +01:00
d47375a70e fix ebreak CSR update 2021-11-13 12:47:23 +01:00
d31b4ef5a8 fix MISA val 2021-11-11 12:58:57 +01:00
7452c5df43 add TGC_D_XRB_NN definition 2021-11-11 12:16:35 +01:00
43d7b99905 revert pmp check implementation 2021-11-11 09:58:19 +01:00
f90c48e881 adapt to changed define names 2021-11-11 08:33:35 +01:00
2d7973520b fix mip handling 2021-11-09 19:47:34 +01:00
fd98ad95f6 rework PMP check and fix MISA for TGC_D 2021-11-09 15:55:22 +01:00
bfa8166223 fix wrong template class name 2021-11-08 10:44:33 +01:00
c42e336509 fix proper debug mode handling (#267 & #268) 2021-11-07 17:48:44 +01:00
49d09a05d7 fix access rights to debug CSR register (#268) 2021-11-07 16:45:10 +01:00
459794b863 add proper handling of store access fault (hart_mu_p) 2021-11-06 13:29:11 +01:00
039746112b fix exception behavior 2021-11-02 15:10:20 +01:00
ac6d7ea5d4 add debug feature to platform 2021-11-02 11:13:29 +01:00
a89f00da19 fix plugins parameter utilization 2021-11-02 11:03:17 +01:00
ff04ee7807 get rid of the Boost::thread linking 2021-11-02 10:24:34 +01:00
8b6e3abd23 fix hard-code arch in templates 2021-10-30 13:37:17 +02:00
1616f0ac90 remove deprecated functions 2021-10-30 12:57:08 +02:00
a20f39e847 update core definitions to include Zicsr and Zifencei (#276) 2021-10-30 12:56:31 +02:00
334d3fb296 adapt to SCC changes 2021-10-21 22:53:16 +02:00
eb2ca33e5a remove unused sources 2021-10-12 15:17:56 +02:00
0ea4cba1ca add dynamic plugin loading 2021-10-12 14:24:55 +02:00
1d13c8196e fix wrong PGMASK usage 2021-10-11 10:40:01 +02:00
ee6e1d4092 Merge remote-tracking branch 'origin/msvc_compat' into develop
Conflicts:
	src/sysc/core_complex.cpp
2021-10-11 09:42:40 +02:00
b17682e50e fix YAML template 2021-10-01 23:49:04 +02:00
5866acf565 update .gitignore 2021-10-01 13:06:10 +02:00
6acf73a40f add template to generate instruction YAML 2021-10-01 13:05:36 +02:00
2f15d9676e fix unaligned instr fetch behavior 2021-09-30 19:27:46 +02:00
d78fcc48e5 use marchid in platform 2021-09-30 19:27:03 +02:00
4186723d37 add marchid setting to CoreDSL description 2021-09-30 19:26:21 +02:00
17ee7b138d update generated TGC-C VM 2021-09-29 00:44:17 +02:00
aa84a27a5b fix JALR alignment in description 2021-09-29 00:43:42 +02:00
438e598a4a remove clutter from core descriptions, added instr alignment setting 2021-09-29 00:03:11 +02:00
174259155d add support for non-compressed ISA 2021-09-23 21:09:52 +02:00
ba9339a50d fix MPP reset value, PMP inactive in U-mode handling and MRET in U-mode 2021-09-21 16:52:40 +02:00
65b4db5eca remove mcounteren in M-mode only platform 2021-09-18 11:40:00 +02:00
0fd82f1f3c add tgc_d_xrb_mac to SC and C++ ISS 2021-09-04 13:04:34 +02:00
a3084456fd rework core definitions 2021-09-04 12:47:07 +02:00
57 changed files with 11031 additions and 14480 deletions

3
.gitignore vendored
View File

@ -30,4 +30,5 @@ language.settings.xml
/.gdbinit
/*.out
/dump.json
/src-gen/
/*.yaml
/*.json

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "gen_input/CoreDSL-Instruction-Set-Description"]
path = gen_input/CoreDSL-Instruction-Set-Description
url = ../CoreDSL-Instruction-Set-Description.git

View File

@ -6,7 +6,9 @@ project(dbt-rise-tgc VERSION 1.0.0)
include(GNUInstallDirs)
find_package(elfio)
find_package(elfio QUIET)
find_package(Boost COMPONENTS coroutine)
find_package(jsoncpp)
if(WITH_LLVM)
if(DEFINED ENV{LLVM_HOME})
@ -27,24 +29,40 @@ endif()
add_subdirectory(softfloat)
# library files
FILE(GLOB TGC_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/iss/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/vm/interp/vm_*.cpp
)
set(LIB_SOURCES
src/iss/plugin/instruction_count.cpp
src/iss/arch/tgc_c.cpp
src/vm/interp/vm_tgc_c.cpp
src/vm/fp_functions.cpp
src/plugin/instruction_count.cpp
src/plugin/cycle_estimate.cpp
${TGC_SOURCES}
)
if(WITH_LLVM)
set(LIB_SOURCES ${LIB_SOURCES}
src/vm/llvm/fp_impl.cpp
#src/vm/llvm/vm_tgf_b.cpp
#src/vm/llvm/vm_tgf_c.cpp
# library files
if(TARGET ${CORE_NAME}_cpp)
list(APPEND LIB_SOURCES ${${CORE_NAME}_OUTPUT_FILES})
else()
FILE(GLOB GEN_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp
)
list(APPEND LIB_SOURCES ${GEN_SOURCES})
endif()
if(TARGET RapidJSON OR TARGET RapidJSON::RapidJSON)
list(APPEND LIB_SOURCES src/iss/plugin/cycle_estimate.cpp src/iss/plugin/pctrace.cpp)
endif()
if(WITH_LLVM)
FILE(GLOB LLVM_GEN_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/llvm/vm_*.cpp
)
list(APPEND LIB_SOURCES ${LLVM_GEN_SOURCES})
endif()
if(WITH_TCC)
FILE(GLOB TCC_GEN_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/vm/tcc/vm_*.cpp
)
list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
endif()
# Define the library
@ -59,20 +77,34 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
target_compile_options(${PROJECT_NAME} PRIVATE /wd4293)
endif()
target_include_directories(${PROJECT_NAME} PUBLIC incl)
target_link_libraries(${PROJECT_NAME} PUBLIC softfloat scc-util jsoncpp)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
target_link_libraries(${PROJECT_NAME} PUBLIC -Wl,--whole-archive dbt-core -Wl,--no-whole-archive)
target_include_directories(${PROJECT_NAME} PUBLIC src)
target_include_directories(${PROJECT_NAME} PUBLIC src-gen)
target_link_libraries(${PROJECT_NAME} PUBLIC softfloat scc-util Boost::coroutine)
if(TARGET jsoncpp::jsoncpp)
target_link_libraries(${PROJECT_NAME} PUBLIC jsoncpp::jsoncpp)
else()
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-core)
target_link_libraries(${PROJECT_NAME} PUBLIC jsoncpp)
endif()
if(TARGET CONAN_PKG::elfio)
target_link_libraries(${PROJECT_NAME} PUBLIC CONAN_PKG::elfio)
elseif(TARGET elfio::elfio)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND BUILD_SHARED_LIBS)
target_link_libraries(${PROJECT_NAME} PUBLIC -Wl,--whole-archive dbt-rise-core -Wl,--no-whole-archive)
else()
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-core)
endif()
if(TARGET elfio::elfio)
target_link_libraries(${PROJECT_NAME} PUBLIC elfio::elfio)
else()
message(FATAL_ERROR "No elfio library found, maybe a find_package() call is missing")
endif()
if(TARGET lz4::lz4)
target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_LZ4)
target_link_libraries(${PROJECT_NAME} PUBLIC lz4::lz4)
endif()
if(TARGET RapidJSON::RapidJSON)
target_link_libraries(${PROJECT_NAME} PUBLIC RapidJSON::RapidJSON)
elseif(TARGET RapidJSON)
target_link_libraries(${PROJECT_NAME} PUBLIC RapidJSON)
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
@ -99,18 +131,37 @@ project(tgc-sim)
find_package(Boost COMPONENTS program_options thread REQUIRED)
add_executable(${PROJECT_NAME} src/main.cpp)
# This sets the include directory for the reference project. This is the -I flag in gcc.
if(TARGET ${CORE_NAME}_cpp)
list(APPEND TGC_SOURCES ${${CORE_NAME}_OUTPUT_FILES})
else()
FILE(GLOB TGC_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp
)
list(APPEND TGC_SOURCES ${GEN_SOURCES})
endif()
foreach(F IN LISTS TGC_SOURCES)
if (${F} MATCHES ".*/arch/([^/]*)\.cpp")
string(REGEX REPLACE ".*/([^/]*)\.cpp" "\\1" CORE_NAME_LC ${F})
string(TOUPPER ${CORE_NAME_LC} CORE_NAME)
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
endif()
endforeach()
if(WITH_LLVM)
target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_LLVM)
target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs})
endif()
if(WITH_TCC)
target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_TCC)
endif()
# Links the target exe against the libraries
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc)
if(TARGET Boost::program_options)
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::program_options Boost::thread)
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::program_options)
else()
target_link_libraries(${PROJECT_NAME} PUBLIC ${BOOST_program_options_LIBRARY} ${BOOST_thread_LIBRARY})
target_link_libraries(${PROJECT_NAME} PUBLIC ${BOOST_program_options_LIBRARY})
endif()
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS})
if (Tcmalloc_FOUND)
@ -129,28 +180,24 @@ install(TARGETS tgc-sim
###############################################################################
#
###############################################################################
if(TARGET scc-sysc)
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)
foreach(F IN LISTS TGC_SOURCES)
if (${F} MATCHES ".*/arch/([^/]*)\.cpp")
string(REGEX REPLACE ".*/([^/]*)\.cpp" "\\1" CORE_NAME_LC ${F})
string(TOUPPER ${CORE_NAME_LC} CORE_NAME)
target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
endif()
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)
endforeach()
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc scc-sysc)
if(WITH_LLVM)
target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs})
endif()
set(LIB_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/incl/sysc/core_complex.h)
set(LIB_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/sysc/core_complex.h)
set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
FRAMEWORK FALSE

3
contrib/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/results
/cwr
/*.xml

43
contrib/README.md Normal file
View File

@ -0,0 +1,43 @@
# Notes
* requires conan version 1.59
* requires decent cmake version 3.23
Setup for tcsh:
```
git clone --recursive -b develop https://git.minres.com/TGFS/TGC-ISS.git
cd TGC-ISS/
setenv TGFS_INSTALL_ROOT `pwd`/install
setenv COWAREHOME <your SNPS PA installation>
setenv SNPSLMD_LICENSE_FILE <your SNPS PA license file>
source $COWAREHOME/SLS/linux/setup.csh pae
setenv SNPS_ENABLE_MEM_ON_DEMAND_IN_GENERIC_MEM 1
setenv PATH $COWAREHOME/common/bin/:${PATH}
setenv CC $COWAREHOME/SLS/linux/common/bin/gcc
setenv CXX $COWAREHOME/SLS/linux/common/bin/g++
cmake -S . -B build/PA -DCMAKE_BUILD_TYPE=Debug -DUSE_CWR_SYSTEMC=ON -DBUILD_SHARED_LIBS=ON \
-DCODEGEN=OFF -DCMAKE_INSTALL_PREFIX=${TGFS_INSTALL_ROOT}
cmake --build build/PA --target install -j16
cd dbt-rise-tgc/contrib
# import the TGC core itself
pct tgc_import_tb.tcl
```
Setup for bash:
```
git clone --recursive -b develop https://git.minres.com/TGFS/TGC-ISS.git
cd TGC-ISS/
export TGFS_INSTALL_ROOT `pwd`/install
module load tools/pa/T-2022.06
export SNPS_ENABLE_MEM_ON_DEMAND_IN_GENERIC_MEM=1
export CC=$COWAREHOME/SLS/linux/common/bin/gcc
export CXX=$COWAREHOME/SLS/linux/common/bin/g++
cmake -S . -B build/PA -DCMAKE_BUILD_TYPE=Debug -DUSE_CWR_SYSTEMC=ON -DBUILD_SHARED_LIBS=ON \
-DCODEGEN=OFF -DCMAKE_INSTALL_PREFIX=${TGFS_INSTALL_ROOT}
cmake --build build/PA --target install -j16
cd dbt-rise-tgc/contrib
# import the TGC core itself
pct tgc_import_tb.tcl
```

View File

@ -16,7 +16,7 @@ namespace eval Specification {
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"]
set linkerOptions [concat $linkerOptions "-Wl,-rpath,${libdir} -L${libdir} -ldbt-rise-tgc_sc -lscc-sysc"]
}
default {
puts stderr "ERROR: \"$target\" is not supported, [::scsh::version]"

2092
contrib/hello.dis Normal file

File diff suppressed because it is too large Load Diff

BIN
contrib/hello.elf Executable file

Binary file not shown.

BIN
contrib/minres.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -6,14 +6,11 @@ proc getScriptDirectory {} {
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 encap_name sysc::tgfs::${top_design_name}
set clocks clk_i
set resets rst_i
set model_prefix "i_"
@ -28,7 +25,8 @@ set model_postfix ""
::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
::pct::set_import_encap_prop_as_extra_prop_flag true
::pct::load_modules --set-category modules ${scriptDir}/tgc_import.cc
# Set Port Protocols correctly
set block ${top_design_name}
@ -38,13 +36,15 @@ foreach clock ${clocks} {
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
#::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_encap_build_script $block/${encap_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}
::pct::create_instance SYSTEM_LIBRARY:${top_design_name} ${hardware} ${model_prefix}${top_design_name}${model_postfix} ${encap_name} ${encap_name}()
::pct::set_bounds i_${top_design_name} 200 300 100 400
::pct::set_image i_${top_design_name} "$scriptDir/minres.png" center center false true
# export the result as component
::pct::export_system_library ${top_design_name} ${top_design_name}.xml

71
contrib/tgc_import_tb.tcl Normal file
View File

@ -0,0 +1,71 @@
source tgc_import.tcl
set hardware /HARDWARE/HW/HW
set FW_name ${scriptDir}/hello.elf
puts "instantiate testbench elements"
::paultra::add_hw_instance GenericIPlib:Memory_Generic -inst_name i_Memory_Generic
::pct::set_param_value i_Memory_Generic/MEM:protocol {Protocol Common Parameters} address_width 30
::pct::set_param_value i_Memory_Generic {Scml Properties} /timing/LT/clock_period_in_ns 1
::pct::set_param_value i_Memory_Generic {Scml Properties} /timing/read/cmd_accept_cycles 1
::pct::set_param_value i_Memory_Generic {Scml Properties} /timing/write/cmd_accept_cycles 1
::pct::set_bounds i_Memory_Generic 1000 300 100 100
::paultra::add_hw_instance Bus:Bus -inst_name i_Bus
::BLWizard::generateFramework i_Bus SBLTLM2FT * {} \
{ common_configuration:BackBone:/advanced/num_resources_per_target:1 }
::pct::set_bounds i_Bus 700 300 100 400
::pct::create_connection C_ibus i_core_complex/ibus i_Bus/i_core_complex_ibus
::pct::set_location_on_owner i_Bus/i_core_complex_ibus 10
::pct::create_connection C_dbus i_core_complex/dbus i_Bus/i_core_complex_dbus
::pct::set_location_on_owner i_Bus/i_core_complex_dbus 10
::pct::create_connection C_mem i_Bus/i_Memory_Generic_MEM i_Memory_Generic/MEM
puts "instantiating clock manager"
set clock "Clk"
::hw::create_hw_instance "" GenericIPlib:ClockGenerator ${clock}_clock
::pct::set_bounds ${clock}_clock 100 100 100 100
::pct::set_param_value $hardware/${clock}_clock {Constructor Arguments} period 1000
::pct::set_param_value $hardware/${clock}_clock {Constructor Arguments} period_unit sc_core::SC_PS
puts "instantiating reset manager"
set reset "Rst"
::hw::create_hw_instance "" GenericIPlib:ResetGenerator ${reset}_reset
::pct::set_param_value $hardware/${reset}_reset {Constructor Arguments} start_time 0
::pct::set_param_value $hardware/${reset}_reset {Constructor Arguments} start_time_unit sc_core::SC_PS
::pct::set_param_value $hardware/${reset}_reset {Constructor Arguments} duration 10000
::pct::set_param_value $hardware/${reset}_reset {Constructor Arguments} duration_unit sc_core::SC_PS
::pct::set_param_value $hardware/${reset}_reset {Constructor Arguments} active_level true
::pct::set_bounds ${reset}_reset 300 100 100 100
puts "connecting reset/clock"
::pct::create_connection C_clk . Clk_clock/CLK i_core_complex/clk_i
::pct::add_ports_to_connection C_clk i_Bus/Clk
::pct::add_ports_to_connection C_clk i_Memory_Generic/CLK
::pct::create_connection C_rst . Rst_reset/RST i_core_complex/rst_i
::pct::add_ports_to_connection C_rst i_Bus/Rst
puts "setting parameters for DBT-RISE-TGC/Bus and memory components"
::pct::set_param_value $hardware/i_${top_design_name} {Extra properties} elf_file ${FW_name}
::pct::set_address $hardware/i_${top_design_name}/ibus:i_Memory_Generic/MEM 0x0
::pct::set_address $hardware/i_${top_design_name}/dbus:i_Memory_Generic/MEM 0x0
::BLWizard::updateFramework i_Bus {} { common_configuration:BackBone:/advanced/num_resources_per_target:1 }
::pct::set_main_configuration Default {{#include <scc/report.h>} {::scc::init_logging(::scc::LogConfig().logLevel(::scc::log::INFO).coloredOutput(false).logAsync(false));} {} {} {}}
::pct::set_main_configuration Debug {{#include <scc/report.h>} {::scc::init_logging(::scc::LogConfig().logLevel(::scc::log::DEBUG).coloredOutput(false).logAsync(false));} {} {} {}}
::pct::create_simulation_build_config Debug
::pct::set_simulation_build_project_setting Debug "Main Configuration" Default
# add build settings and save design for next steps
#::pct::set_simulation_build_project_setting "Debug" "Linker Flags" "-Wl,-z,muldefs $::env(VERILATOR_ROOT)/include/verilated.cpp $::env(VERILATOR_ROOT)/include/verilated_vcd_sc.cpp $::env(VERILATOR_ROOT)/include/verilated_vcd_c.cpp"
#::pct::set_simulation_build_project_setting "Debug" "Include Paths" $::env(VERILATOR_ROOT)/include/
#::simulation::set_simulation_property Simulation [list run_for_duration:200ns results_dir:results/test_0 "TLM Port Trace:true"]
#::simulation::run_simulation Simulation
#::pct::set_simulation_build_project_setting Debug {Export Type} {STATIC NETLIST}
#::pct::set_simulation_build_project_setting Debug {Encapsulated Netlist} false
#::pct::export_system "export"
#::cd "export"
#::scsh::open-project
#::scsh::build
#::scsh::elab sim
::pct::save_system testbench.xml

View File

@ -1 +1,2 @@
/src-gen/
/CoreDSL-Instruction-Set-Description

13
gen_input/TGC_C.core_desc Normal file
View File

@ -0,0 +1,13 @@
import "RV32I.core_desc"
import "RVM.core_desc"
import "RVC.core_desc"
Core TGC_C provides RV32I, Zicsr, Zifencei, RV32M, RV32IC {
architectural_state {
XLEN=32;
// definitions for the architecture wrapper
// XL ZYXWVUTSRQPONMLKJIHGFEDCBA
unsigned MISA_VAL = 0b01000000000000000001000100000100;
unsigned MARCHID_VAL = 0x80000003;
}
}

View File

@ -1,37 +0,0 @@
import "CoreDSL-Instruction-Set-Description/RV32I.core_desc"
import "CoreDSL-Instruction-Set-Description/RVM.core_desc"
import "CoreDSL-Instruction-Set-Description/RVC.core_desc"
Core TGC_B provides RV32I {
architectural_state {
unsigned XLEN=32;
unsigned PCLEN=32;
// definitions for the architecture wrapper
// XL ZYXWVUTSRQPONMLKJIHGFEDCBA
unsigned MISA_VAL = 0b01000000000000000000000100000000;
unsigned PGSIZE = 0x1000; //1 << 12;
unsigned PGMASK = 0xfff; //PGSIZE-1
}
}
Core TGC_C provides RV32I, RV32M, RV32IC {
architectural_state {
unsigned XLEN=32;
unsigned PCLEN=32;
// definitions for the architecture wrapper
// XL ZYXWVUTSRQPONMLKJIHGFEDCBA
unsigned MISA_VAL = 0b01000000000000000001000100000100;
unsigned PGSIZE = 0x1000; //1 << 12;
unsigned PGMASK = 0xfff; //PGSIZE-1
}
}
Core TGC_D provides RV32I, RV32M, RV32IC {
architectural_state {
unsigned XLEN=32;
unsigned PCLEN=32;
// definitions for the architecture wrapper
// XL ZYXWVUTSRQPONMLKJIHGFEDCBA
unsigned MISA_VAL = 0b01000000000000000001000100000100;
}
}

View File

@ -33,13 +33,13 @@
def getRegisterSizes(){
def regs = registers.collect{it.size}
regs[-1]=64 // correct for NEXT_PC
regs+=[32, 32, 64, 64, 64] // append TRAP_STATE, PENDING_TRAP, ICOUNT, CYCLE, INSTRET
regs+=[32,32, 64, 64, 64, 32, 32] // append TRAP_STATE, PENDING_TRAP, ICOUNT, CYCLE, INSTRET, INSTRUCTION, LAST_BRANCH
return regs
}
%>
#include "${coreDef.name.toLowerCase()}.h"
#include "util/ities.h"
#include <util/logging.h>
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
#include <cstdio>
#include <cstring>
#include <fstream>
@ -51,19 +51,19 @@ constexpr std::array<const char*, ${registers.size}> iss::arch::traits<iss::a
constexpr std::array<const uint32_t, ${getRegisterSizes().size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_bit_widths;
constexpr std::array<const uint32_t, ${getRegisterSizes().size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_byte_offsets;
${coreDef.name.toLowerCase()}::${coreDef.name.toLowerCase()}() {
reg.icount = 0;
}
${coreDef.name.toLowerCase()}::${coreDef.name.toLowerCase()}() = default;
${coreDef.name.toLowerCase()}::~${coreDef.name.toLowerCase()}() = default;
void ${coreDef.name.toLowerCase()}::reset(uint64_t address) {
for(size_t i=0; i<traits<${coreDef.name.toLowerCase()}>::NUM_REGS; ++i) set_reg(i, std::vector<uint8_t>(sizeof(traits<${coreDef.name.toLowerCase()}>::reg_t),0));
auto base_ptr = reinterpret_cast<traits<${coreDef.name.toLowerCase()}>::reg_t*>(get_regs_base_ptr());
for(size_t i=0; i<traits<${coreDef.name.toLowerCase()}>::NUM_REGS; ++i)
*(base_ptr+i)=0;
reg.PC=address;
reg.NEXT_PC=reg.PC;
reg.PRIV=0x3;
reg.trap_state=0;
reg.icount=0;
trap_state=0;
icount=0;
}
uint8_t *${coreDef.name.toLowerCase()}::get_regs_base_ptr() {

View File

@ -37,7 +37,7 @@ def nativeTypeSize(int size){
}
def getRegisterSizes(){
def regs = registers.collect{nativeTypeSize(it.size)}
regs+=[32,32, 64, 64, 64] // append TRAP_STATE, PENDING_TRAP, ICOUNT, CYCLE, INSTRET
regs+=[32,32, 64, 64, 64, 32, 32] // append TRAP_STATE, PENDING_TRAP, ICOUNT, CYCLE, INSTRET, INSTRUCTION, LAST_BRANCH
return regs
}
def getRegisterOffsets(){
@ -91,12 +91,7 @@ template <> struct traits<${coreDef.name.toLowerCase()}> {
constexpr static unsigned FP_REGS_SIZE = ${constants.find {it.name=='FLEN'}?.value?:0};
enum reg_e {
${registers.collect{it.name}.join(', ')}, NUM_REGS,
TRAP_STATE=NUM_REGS,
PENDING_TRAP,
ICOUNT,
CYCLE,
INSTRET
${registers.collect{it.name}.join(', ')}, NUM_REGS, TRAP_STATE=NUM_REGS, PENDING_TRAP, ICOUNT, CYCLE, INSTRET, INSTRUCTION, LAST_BRANCH
};
using reg_t = uint${addrDataWidth}_t;
@ -140,16 +135,8 @@ struct ${coreDef.name.toLowerCase()}: public arch_if {
void reset(uint64_t address=0) override;
uint8_t* get_regs_base_ptr() override;
/// deprecated
void get_reg(short idx, std::vector<uint8_t>& value) override {}
void set_reg(short idx, const std::vector<uint8_t>& value) override {}
/// deprecated
bool get_flag(int flag) override {return false;}
void set_flag(int, bool value) override {};
/// deprecated
void update_flags(operations op, uint64_t opr1, uint64_t opr2) override {};
inline uint64_t get_icount() { return reg.icount; }
inline uint64_t get_icount() { return icount; }
inline bool should_stop() { return interrupt_sim; }
@ -167,9 +154,9 @@ struct ${coreDef.name.toLowerCase()}: public arch_if {
virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; }
inline uint32_t get_last_branch() { return reg.last_branch; }
inline uint32_t get_last_branch() { return last_branch; }
protected:
#pragma pack(push, 1)
struct ${coreDef.name}_regs {<%
registers.each { reg -> if(reg.size>0) {%>
@ -179,9 +166,16 @@ protected:
uint64_t icount = 0;
uint64_t cycle = 0;
uint64_t instret = 0;
uint32_t last_branch;
uint32_t instruction = 0;
uint32_t last_branch = 0;
} reg;
#pragma pack(pop)
uint32_t trap_state = 0, pending_trap = 0;
uint64_t icount = 0;
uint64_t cycle = 0;
uint64_t instret = 0;
uint32_t instruction = 0;
uint32_t last_branch = 0;
std::array<address_type, 4> addr_mode;
uint64_t interrupt_sim=0;

View File

@ -3,7 +3,10 @@
{
"name" : "${instr.name}",
"size" : ${instr.length},
"delay" : ${generator.hasAttribute(instr.instruction, com.minres.coredsl.coreDsl.InstrAttribute.COND)?[1,1]:1}
"encoding": "${instr.encoding}",
"mask": "${instr.mask}",
"branch": ${instr.modifiesPC},
"delay" : ${instr.isConditional?"[1,1]":"1"}
}<%}%>
]
}

View File

@ -0,0 +1,86 @@
#include "${coreDef.name.toLowerCase()}.h"
#include <vector>
#include <array>
#include <cstdlib>
#include <algorithm>
namespace iss {
namespace arch {
namespace {
// according to
// https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation
#ifdef __GCC__
constexpr size_t bit_count(uint32_t u) { return __builtin_popcount(u); }
#elif __cplusplus < 201402L
constexpr size_t uCount(uint32_t u) { return u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); }
constexpr size_t bit_count(uint32_t u) { return ((uCount(u) + (uCount(u) >> 3)) & 030707070707) % 63; }
#else
constexpr size_t bit_count(uint32_t u) {
size_t uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
return ((uCount + (uCount >> 3)) & 030707070707) % 63;
}
#endif
using opcode_e = traits<${coreDef.name.toLowerCase()}>::opcode_e;
/****************************************************************************
* start opcode definitions
****************************************************************************/
struct instruction_desriptor {
size_t length;
uint32_t value;
uint32_t mask;
opcode_e op;
};
const std::array<instruction_desriptor, ${instructions.size}> instr_descr = {{
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
{${instr.length}, ${instr.encoding}, ${instr.mask}, opcode_e::${instr.instruction.name}},<%}%>
}};
}
template<>
struct instruction_decoder<${coreDef.name.toLowerCase()}> {
using opcode_e = traits<${coreDef.name.toLowerCase()}>::opcode_e;
using code_word_t=traits<${coreDef.name.toLowerCase()}>::code_word_t;
struct instruction_pattern {
uint32_t value;
uint32_t mask;
opcode_e id;
};
std::array<std::vector<instruction_pattern>, 4> qlut;
template<typename T>
unsigned decode_instruction(T);
instruction_decoder() {
for (auto instr : instr_descr) {
auto quadrant = instr.value & 0x3;
qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
}
for(auto& lut: qlut){
std::sort(std::begin(lut), std::end(lut), [](instruction_pattern const& a, instruction_pattern const& b){
return bit_count(a.mask) < bit_count(b.mask);
});
}
}
};
template<>
unsigned instruction_decoder<${coreDef.name.toLowerCase()}>::decode_instruction<traits<${coreDef.name.toLowerCase()}>::code_word_t>(traits<${coreDef.name.toLowerCase()}>::code_word_t instr){
auto res = std::find_if(std::begin(qlut[instr&0x3]), std::end(qlut[instr&0x3]), [instr](instruction_pattern const& e){
return !((instr&e.mask) ^ e.value );
});
return static_cast<unsigned>(res!=std::end(qlut[instr&0x3])? res->id : opcode_e::MAX_OPCODE);
}
std::unique_ptr<instruction_decoder<${coreDef.name.toLowerCase()}>> traits<${coreDef.name.toLowerCase()}>::get_decoder(){
return std::make_unique<instruction_decoder<${coreDef.name.toLowerCase()}>>();
}
}
}

View File

@ -0,0 +1,17 @@
<% def getInstructionGroups() {
def instrGroups = [:]
instructions.each {
def groupName = it['instruction'].eContainer().name
if(!instrGroups.containsKey(groupName)) {
instrGroups[groupName]=[]
}
instrGroups[groupName]+=it;
}
instrGroups
}%><%getInstructionGroups().each{name, instrList -> %>
${name}: <% instrList.findAll{!it.instruction.name.startsWith("__")}.each { %>
- ${it.instruction.name}:
encoding: ${it.encoding}
mask: ${it.mask}<%if(it.attributes.size) {%>
attributes: ${it.attributes}<%}}}%>

View File

@ -29,16 +29,23 @@
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
<%
import com.minres.coredsl.util.BigIntegerWithRadix
#include "../fp_functions.h"
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
#include <iss/arch/riscv_hart_m_p.h>
def nativeTypeSize(int size){
if(size<=8) return 8; else if(size<=16) return 16; else if(size<=32) return 32; else return 64;
}
%>
#include <iss/debugger/gdb_session.h>
#include <iss/debugger/server.h>
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
#include <iss/arch/riscv_hart_m_p.h>
#include <iss/iss.h>
#include <iss/interp/vm_base.h>
#include <util/logging.h>
#include <sstream>
#include <boost/coroutine2/all.hpp>
#include <functional>
#ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
@ -53,6 +60,7 @@ namespace interp {
namespace ${coreDef.name.toLowerCase()} {
using namespace iss::arch;
using namespace iss::debugger;
using namespace std::placeholders;
template <typename ARCH> class vm_impl : public iss::interp::vm_base<ARCH> {
public:
@ -64,6 +72,7 @@ public:
using addr_t = typename super::addr_t;
using reg_t = typename traits::reg_t;
using mem_type_e = typename traits::mem_type_e;
using opcode_e = typename traits::opcode_e;
vm_impl();
@ -83,16 +92,19 @@ protected:
using compile_ret_t = virt_addr_t;
using compile_func = compile_ret_t (this_class::*)(virt_addr_t &pc, code_word_t instr);
inline const char *name(size_t index){return traits::reg_aliases.at(index);}
inline const char *name(size_t index){return index<traits::reg_aliases.size()?traits::reg_aliases[index]:"illegal";}
compile_func decode_inst(code_word_t instr) ;
typename arch::traits<ARCH>::opcode_e decode_inst_id(code_word_t instr);
virt_addr_t execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit) override;
// some compile time constants
// enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 };
enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 };
enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 };
enum { LUT_SIZE = 1 << util::bit_count(EXTR_MASK32), LUT_SIZE_C = 1 << util::bit_count(EXTR_MASK16) };
enum {
LUT_SIZE = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK32)),
LUT_SIZE_C = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK16))
};
std::array<compile_func, LUT_SIZE> lut;
@ -102,15 +114,15 @@ protected:
struct instruction_pattern {
uint32_t value;
uint32_t mask;
compile_func opc;
typename arch::traits<ARCH>::opcode_e id;
};
std::array<std::vector<instruction_pattern>, 4> qlut;
inline void raise(uint16_t trap_id, uint16_t cause){
auto trap_val = 0x80ULL << 24 | (cause << 16) | trap_id;
this->template get_reg<uint32_t>(traits::TRAP_STATE) = trap_val;
this->template get_reg<uint32_t>(traits::NEXT_PC) = std::numeric_limits<uint32_t>::max();
this->core.trap_state = trap_val;
this->template get_reg<uint${addrDataWidth}_t>(traits::NEXT_PC) = std::numeric_limits<uint${addrDataWidth}_t>::max();
}
inline void leave(unsigned lvl){
@ -121,44 +133,10 @@ protected:
this->core.wait_until(type);
}
template<typename T>
T& pc_assign(T& val){super::ex_info.branch_taken=true; return val;}
inline uint8_t readSpace1(typename super::mem_type_e space, uint64_t addr){
auto ret = super::template read_mem<uint8_t>(space, addr);
if(this->template get_reg<uint32_t>(traits::TRAP_STATE)) throw 0;
return ret;
}
inline uint16_t readSpace2(typename super::mem_type_e space, uint64_t addr){
auto ret = super::template read_mem<uint16_t>(space, addr);
if(this->template get_reg<uint32_t>(traits::TRAP_STATE)) throw 0;
return ret;
}
inline uint32_t readSpace4(typename super::mem_type_e space, uint64_t addr){
auto ret = super::template read_mem<uint32_t>(space, addr);
if(this->template get_reg<uint32_t>(traits::TRAP_STATE)) throw 0;
return ret;
}
inline uint64_t readSpace8(typename super::mem_type_e space, uint64_t addr){
auto ret = super::template read_mem<uint64_t>(space, addr);
if(this->template get_reg<uint32_t>(traits::TRAP_STATE)) throw 0;
return ret;
}
inline void writeSpace1(typename super::mem_type_e space, uint64_t addr, uint8_t data){
super::write_mem(space, addr, data);
if(this->template get_reg<uint32_t>(traits::TRAP_STATE)) throw 0;
}
inline void writeSpace2(typename super::mem_type_e space, uint64_t addr, uint16_t data){
super::write_mem(space, addr, data);
if(this->template get_reg<uint32_t>(traits::TRAP_STATE)) throw 0;
}
inline void writeSpace4(typename super::mem_type_e space, uint64_t addr, uint32_t data){
super::write_mem(space, addr, data);
if(this->template get_reg<uint32_t>(traits::TRAP_STATE)) throw 0;
}
inline void writeSpace8(typename super::mem_type_e space, uint64_t addr, uint64_t data){
super::write_mem(space, addr, data);
if(this->template get_reg<uint32_t>(traits::TRAP_STATE)) throw 0;
}
using yield_t = boost::coroutines2::coroutine<void>::push_type;
using coro_t = boost::coroutines2::coroutine<void>::pull_type;
std::vector<coro_t> spawn_blocks;
template<unsigned W, typename U, typename S = typename std::make_signed<U>::type>
inline S sext(U from) {
auto mask = (1ULL<<W) - 1;
@ -166,6 +144,19 @@ protected:
return (from & mask) | ((from & sign_mask) ? ~mask : 0);
}
inline void process_spawn_blocks() {
if(spawn_blocks.size()==0) return;
for(auto it = std::begin(spawn_blocks); it!=std::end(spawn_blocks);)
if(*it){
(*it)();
++it;
} else
spawn_blocks.erase(it);
}
<%functions.each{ it.eachLine { %>
${it}<%}%>
<%}%>
private:
/****************************************************************************
* start opcode definitions
@ -174,76 +165,15 @@ private:
size_t length;
uint32_t value;
uint32_t mask;
compile_func op;
typename arch::traits<ARCH>::opcode_e op;
};
const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
/* instruction ${instr.instruction.name} */
{${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
{${instr.length}, ${instr.encoding}, ${instr.mask}, arch::traits<ARCH>::opcode_e::${instr.instruction.name}},<%}%>
}};
/* instruction definitions */<%instructions.eachWithIndex{instr, idx -> %>
/* instruction ${idx}: ${instr.name} */
compile_ret_t __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr){
// pre execution stuff
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}
<%}%>if(this->disass_enabled){
/* generate console output when executing the command */
<%instr.disass.eachLine{%>${it}
<%}%>
}
// used registers<%instr.usedVariables.each{ k,v->
if(v.isArray) {%>
auto* ${k} = reinterpret_cast<uint${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}]);
<%}}%>// calculate next pc value
*NEXT_PC = *PC + ${instr.length/8};
// execute instruction
try {
<%instr.behavior.eachLine{%>${it}
<%}%>} catch(...){}
// post execution stuff
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, ${idx});
// trap check
if(*trap_state!=0){
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;
return pc;
}
<%}%>
/****************************************************************************
* end opcode definitions
****************************************************************************/
compile_ret_t illegal_intruction(virt_addr_t &pc, code_word_t instr) {
this->do_sync(PRE_SYNC, static_cast<unsigned>(arch::traits<ARCH>::opcode_e::MAX_OPCODE));
uint32_t* PC = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]);
uint32_t* NEXT_PC = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]);
*NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2);
raise(0, 2);
// post execution stuff
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, static_cast<unsigned>(arch::traits<ARCH>::opcode_e::MAX_OPCODE));
auto* trap_state = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::TRAP_STATE]);
// trap check
if(*trap_state!=0){
super::core.enter_trap(*trap_state, pc.val, instr);
}
pc.val=*NEXT_PC;
return pc;
}
static constexpr typename traits::addr_t upper_bits = ~traits::PGMASK;
//static constexpr typename traits::addr_t upper_bits = ~traits::PGMASK;
iss::status fetch_ins(virt_addr_t pc, uint8_t * data){
auto phys_pc = this->core.v2p(pc);
//if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
@ -281,6 +211,7 @@ constexpr size_t bit_count(uint32_t u) {
template <typename ARCH>
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
: vm_base<ARCH>(core, core_id, cluster_id) {
unsigned id=0;
for (auto instr : instr_descr) {
auto quadrant = instr.value & 0x3;
qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
@ -301,36 +232,82 @@ inline bool is_jump_to_self_enabled(finish_cond_e cond){
}
template <typename ARCH>
typename vm_impl<ARCH>::compile_func vm_impl<ARCH>::decode_inst(code_word_t instr){
typename arch::traits<ARCH>::opcode_e vm_impl<ARCH>::decode_inst_id(code_word_t instr){
for(auto& e: qlut[instr&0x3]){
if(!((instr&e.mask) ^ e.value )) return e.opc;
if(!((instr&e.mask) ^ e.value )) return e.id;
}
return &this_class::illegal_intruction;
return arch::traits<ARCH>::opcode_e::MAX_OPCODE;
}
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){
// we fetch at max 4 byte, alignment is 2
code_word_t insn = 0;
auto *const data = (uint8_t *)&insn;
auto pc=start;
auto* PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]);
auto* NEXT_PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]);
auto& trap_state = this->core.trap_state;
auto& icount = this->core.icount;
auto& cycle = this->core.cycle;
auto& instret = this->core.instret;
auto& instr = this->core.instruction;
// we fetch at max 4 byte, alignment is 2
auto *const data = reinterpret_cast<uint8_t*>(&instr);
while(!this->core.should_stop() &&
!(is_count_limit_enabled(cond) && this->core.get_icount() >= icount_limit)){
auto res = fetch_ins(pc, data);
if(res!=iss::Ok){
if(fetch_ins(pc, data)!=iss::Ok){
this->do_sync(POST_SYNC, std::numeric_limits<unsigned>::max());
pc.val = super::core.enter_trap(std::numeric_limits<uint64_t>::max(), pc.val, 0);
} 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);
(instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
auto inst_id = decode_inst_id(instr);
// pre execution stuff
this->core.last_branch = 0;
if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id));
switch(inst_id){<%instructions.eachWithIndex{instr, idx -> %>
case arch::traits<ARCH>::opcode_e::${instr.name}: {
<%instr.fields.eachLine{%>${it}
<%}%>if(this->disass_enabled){
/* generate console output when executing the command */<%instr.disass.eachLine{%>
${it}<%}%>
}
// used registers<%instr.usedVariables.each{ k,v->
if(v.isArray) {%>
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}0]);<% }else{ %>
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}]);
<%}}%>// calculate next pc value
*NEXT_PC = *PC + ${instr.length/8};
// execute instruction<%instr.behavior.eachLine{%>
${it}<%}%>
TRAP_${instr.name}:break;
}// @suppress("No break at end of case")<%}%>
default: {
*NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2);
raise(0, 2);
}
}
// post execution stuff
process_spawn_blocks();
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, static_cast<unsigned>(inst_id));
// if(!this->core.trap_state) // update trap state if there is a pending interrupt
// this->core.trap_state = this->core.pending_trap;
// trap check
if(trap_state!=0){
super::core.enter_trap(trap_state, pc.val, instr);
} else {
icount++;
instret++;
}
cycle++;
pc.val=*NEXT_PC;
this->core.reg.PC = this->core.reg.NEXT_PC;
this->core.trap_state = this->core.pending_trap;
}
}
return pc;
}
} // namespace mnrv32
}
template <>
std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreDef.name.toLowerCase()} *core, unsigned short port, bool dump) {

View File

@ -30,10 +30,10 @@
*
*******************************************************************************/
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
#include <iss/arch/riscv_hart_m_p.h>
#include <iss/debugger/gdb_session.h>
#include <iss/debugger/server.h>
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
#include <iss/arch/riscv_hart_m_p.h>
#include <iss/iss.h>
#include <iss/llvm/vm_base.h>
#include <util/logging.h>

View File

@ -55,10 +55,12 @@ using namespace iss::debugger;
template <typename ARCH> class vm_impl : public iss::tcc::vm_base<ARCH> {
public:
using traits = arch::traits<ARCH>;
using super = typename iss::tcc::vm_base<ARCH>;
using virt_addr_t = typename super::virt_addr_t;
using phys_addr_t = typename super::phys_addr_t;
using code_word_t = typename super::code_word_t;
using mem_type_e = typename traits::mem_type_e;
using addr_t = typename super::addr_t;
using tu_builder = typename super::tu_builder;
@ -82,7 +84,7 @@ protected:
using compile_ret_t = std::tuple<continuation_e>;
using compile_func = compile_ret_t (this_class::*)(virt_addr_t &pc, code_word_t instr, tu_builder&);
inline const char *name(size_t index){return traits<ARCH>::reg_aliases.at(index);}
inline const char *name(size_t index){return traits::reg_aliases.at(index);}
void setup_module(std::string m) override {
super::setup_module(m);
@ -104,10 +106,10 @@ protected:
inline void gen_set_pc(tu_builder& tu, virt_addr_t pc, unsigned reg_num) {
switch(reg_num){
case traits<ARCH>::NEXT_PC:
case traits::NEXT_PC:
tu("*next_pc = {:#x};", pc.val);
break;
case traits<ARCH>::PC:
case traits::PC:
tu("*pc = {:#x};", pc.val);
break;
default:
@ -123,7 +125,7 @@ protected:
// enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 };
enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 };
enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 };
enum { LUT_SIZE = 1 << util::bit_count(EXTR_MASK32), LUT_SIZE_C = 1 << util::bit_count(EXTR_MASK16) };
enum { LUT_SIZE = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK32)), LUT_SIZE_C = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK16)) };
std::array<compile_func, LUT_SIZE> lut;
@ -170,6 +172,12 @@ protected:
}
return lut_val;
}
template<unsigned W, typename U, typename S = typename std::make_signed<U>::type>
inline S sext(U from) {
auto mask = (1ULL<<W) - 1;
auto sign_mask = 1ULL<<(W-1);
return (from & mask) | ((from & sign_mask) ? ~mask : 0);
}
private:
/****************************************************************************
@ -185,14 +193,27 @@ private:
const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{
/* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
/* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */
{${instr.length}, 0b${instr.value}, 0b${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
{${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
}};
/* instruction definitions */<%instructions.eachWithIndex{instr, idx -> %>
/* instruction ${idx}: ${instr.name} */
compile_ret_t __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr, tu_builder& tu){<%instr.code.eachLine{%>
compile_ret_t __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr, tu_builder& tu){
tu("${instr.name}_{:#010x}:", pc.val);
vm_base<ARCH>::gen_sync(tu, PRE_SYNC,${idx});
<%instr.fields.eachLine{%>${it}
<%}%>if(this->disass_enabled){
/* generate console output when executing the command */<%instr.disass.eachLine{%>
${it}<%}%>
}
auto cur_pc_val = tu.constant(pc.val, traits::reg_bit_widths[traits::PC]);
pc=pc+4;
tu.open_scope();<%instr.behavior.eachLine{%>
${it}<%}%>
vm_base<ARCH>::gen_sync(tu, POST_SYNC,${idx});
gen_trap_check(tu);
return returnValue;
}
<%}%>
/****************************************************************************
* end opcode definitions
@ -233,7 +254,7 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt,
// we fetch at max 4 byte, alignment is 2
enum {TRAP_ID=1<<16};
code_word_t insn = 0;
const typename traits<ARCH>::addr_t upper_bits = ~traits<ARCH>::PGMASK;
const typename traits::addr_t upper_bits = ~traits::PGMASK;
phys_addr_t paddr(pc);
auto *const data = (uint8_t *)&insn;
paddr = this->core.v2p(pc);
@ -260,13 +281,13 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt,
template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(tu_builder& tu, uint16_t trap_id, uint16_t cause) {
tu(" *trap_state = {:#x};", 0x80 << 24 | (cause << 16) | trap_id);
tu.store(tu.constant(std::numeric_limits<uint32_t>::max(), 32),traits<ARCH>::LAST_BRANCH);
tu.store(traits::LAST_BRANCH, tu.constant(std::numeric_limits<uint32_t>::max(), 32));
}
template <typename ARCH> void vm_impl<ARCH>::gen_leave_trap(tu_builder& tu, unsigned lvl) {
tu("leave_trap(core_ptr, {});", lvl);
tu.store(tu.read_mem(traits<ARCH>::CSR, (lvl << 8) + 0x41, traits<ARCH>::XLEN),traits<ARCH>::NEXT_PC);
tu.store(tu.constant(std::numeric_limits<uint32_t>::max(), 32),traits<ARCH>::LAST_BRANCH);
tu.store(traits::NEXT_PC, tu.read_mem(traits::CSR, (lvl << 8) + 0x41, traits::XLEN));
tu.store(traits::LAST_BRANCH, tu.constant(std::numeric_limits<uint32_t>::max(), 32));
}
template <typename ARCH> void vm_impl<ARCH>::gen_wait(tu_builder& tu, unsigned type) {
@ -274,8 +295,8 @@ template <typename ARCH> void vm_impl<ARCH>::gen_wait(tu_builder& tu, unsigned t
template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(tu_builder& tu) {
tu("trap_entry:");
tu("enter_trap(core_ptr, *trap_state, *pc);");
tu.store(tu.constant(std::numeric_limits<uint32_t>::max(),32),traits<ARCH>::LAST_BRANCH);
tu("enter_trap(core_ptr, *trap_state, *pc, 0);");
tu.store(traits::LAST_BRANCH, tu.constant(std::numeric_limits<uint32_t>::max(),32));
tu("return *next_pc;");
}

View File

@ -1 +0,0 @@
/tgc_*.h

View File

@ -8,7 +8,7 @@ project("sotfloat" VERSION 3.0.0)
# Set the version number of your project here (format is MAJOR.MINOR.PATCHLEVEL - e.g. 1.0.0)
set(VERSION "3e")
include(Common)
#include(Common)
include(GNUInstallDirs)
set(SPECIALIZATION RISCV)

2
src-gen/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/iss
/vm

100
src/iss/arch/hwl.h Normal file
View File

@ -0,0 +1,100 @@
/*******************************************************************************
* Copyright (C) 2022 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial implementation
******************************************************************************/
#ifndef _RISCV_HART_M_P_HWL_H
#define _RISCV_HART_M_P_HWL_H
#include <iss/vm_types.h>
namespace iss {
namespace arch {
template <typename BASE> class hwl : public BASE {
public:
using base_class = BASE;
using this_class = hwl<BASE>;
using reg_t = typename BASE::reg_t;
hwl();
virtual ~hwl() = default;
protected:
iss::status read_custom_csr_reg(unsigned addr, reg_t &val) override;
iss::status write_custom_csr_reg(unsigned addr, reg_t val) override;
};
template<typename BASE>
inline hwl<BASE>::hwl() {
for (unsigned addr = 0x800; addr < 0x803; ++addr){
this->register_custom_csr_rd(addr);
this->register_custom_csr_wr(addr);
}
for (unsigned addr = 0x804; addr < 0x807; ++addr){
this->register_custom_csr_rd(addr);
this->register_custom_csr_wr(addr);
}
}
template<typename BASE>
inline iss::status iss::arch::hwl<BASE>::read_custom_csr_reg(unsigned addr, reg_t &val) {
switch(addr){
case 0x800: val = this->reg.lpstart0; break;
case 0x801: val = this->reg.lpend0; break;
case 0x802: val = this->reg.lpcount0; break;
case 0x804: val = this->reg.lpstart1; break;
case 0x805: val = this->reg.lpend1; break;
case 0x806: val = this->reg.lpcount1; break;
}
return iss::Ok;
}
template<typename BASE>
inline iss::status iss::arch::hwl<BASE>::write_custom_csr_reg(unsigned addr, reg_t val) {
switch(addr){
case 0x800: this->reg.lpstart0 = val; break;
case 0x801: this->reg.lpend0 = val; break;
case 0x802: this->reg.lpcount0 = val; break;
case 0x804: this->reg.lpstart1 = val; break;
case 0x805: this->reg.lpend1 = val; break;
case 0x806: this->reg.lpcount1 = val; break;
}
return iss::Ok;
}
} // namespace arch
} // namespace iss
#endif /* _RISCV_HART_M_P_H */

View File

@ -43,18 +43,26 @@ namespace arch {
enum { tohost_dflt = 0xF0001000, fromhost_dflt = 0xF0001040 };
enum features_e{FEAT_NONE, FEAT_PMP=1, FEAT_EXT_N=2, FEAT_CLIC=4, FEAT_DEBUG=8, FEAT_TCM=16};
enum riscv_csr {
/* user-level CSR */
// User Trap Setup
ustatus = 0x000,
uie = 0x004,
utvec = 0x005,
utvt = 0x007, //CLIC
// User Trap Handling
uscratch = 0x040,
uepc = 0x041,
ucause = 0x042,
utval = 0x043,
uip = 0x044,
uxnti = 0x045, //CLIC
uintstatus = 0xCB1, // MRW Current interrupt levels (CLIC) - addr subject to change
uintthresh = 0x047, // MRW Interrupt-level threshold (CLIC) - addr subject to change
uscratchcsw = 0x048, // MRW Conditional scratch swap on priv mode change (CLIC)
uscratchcswl = 0x049, // MRW Conditional scratch swap on level change (CLIC)
// User Floating-Point CSRs
fflags = 0x001,
frm = 0x002,
@ -112,11 +120,10 @@ enum riscv_csr {
mtval = 0x343,
mip = 0x344,
mxnti = 0x345, //CLIC
mintstatus = 0x346, // MRW Current interrupt levels (CLIC) - addr subject to change
mintstatus = 0xFB1, // MRW Current interrupt levels (CLIC) - addr subject to change
mintthresh = 0x347, // MRW Interrupt-level threshold (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
pmpcfg0 = 0x3A0,
pmpcfg1 = 0x3A1,
@ -164,7 +171,8 @@ enum riscv_csr {
// Debug Mode Registers
dcsr = 0x7B0,
dpc = 0x7B1,
dscratch = 0x7B2
dscratch0 = 0x7B2,
dscratch1 = 0x7B3
};
@ -185,7 +193,7 @@ enum {
template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); }
enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3 };
enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3, PRIV_D = 4};
enum {
ISA_A = 1,
@ -211,6 +219,15 @@ struct vm_info {
bool is_active() { return levels; }
};
struct feature_config {
uint64_t clic_base{0xc0000000};
unsigned clic_int_ctl_bits{4};
unsigned clic_num_irq{16};
unsigned clic_num_trigger{0};
uint64_t tcm_base{0x10000000};
uint64_t tcm_size{0x8000};
};
class trap_load_access_fault : public trap_access {
public:
trap_load_access_fault(uint64_t badaddr)
@ -236,6 +253,49 @@ public:
trap_store_page_fault(uint64_t badaddr)
: trap_access(15 << 16, badaddr) {}
};
inline void read_reg_uint32(uint64_t offs, uint32_t& reg, uint8_t *const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
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;
}
}
inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t *const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
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;
}
}
}
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2017, 2018, 2021 MINRES Technologies GmbH
* Copyright (C) 2017 - 2023 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -145,7 +145,7 @@ public:
mstatus_t mstatus;
static const reg_t mstatus_reset_val = 0;
static const reg_t mstatus_reset_val = 0x1800;
void write_mstatus(T val, unsigned priv_lvl) {
auto mask = get_mask(priv_lvl);
@ -293,33 +293,25 @@ public:
std::pair<uint64_t, bool> load_file(std::string name, int type = -1) override;
virtual phys_addr_t virt2phys(const iss::addr_t &addr) override;
phys_addr_t virt2phys(const iss::addr_t &addr) override;
iss::status read(const address_type type, const access_type access, const uint32_t space,
const uint64_t addr, const unsigned length, uint8_t *const data) override;
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;
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, uint64_t instr) override;
virtual uint64_t leave_trap(uint64_t flags) override;
uint64_t enter_trap(uint64_t flags) override { return riscv_hart_msu_vp::enter_trap(flags, fault_data, fault_data); }
uint64_t enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) override;
uint64_t leave_trap(uint64_t flags) override;
void wait_until(uint64_t flags) 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:{}]",
pc, instr, lvl[this->reg.PRIV], (reg_t)state.mstatus, this->reg.ccount);
pc, instr, lvl[this->reg.PRIV], (reg_t)state.mstatus, this->icount + cycle_offset);
};
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;
}
@ -340,7 +332,17 @@ protected:
virtual uint64_t get_next_pc() { return arch.get_next_pc(); };
virtual void set_curr_instr_cycles(unsigned cycles) { arch.cycle_offset += cycles - 1; };
uint64_t get_instr_word() override { return arch.instruction; }
uint64_t get_instr_count() { return arch.icount; }
uint64_t get_pendig_traps() override { return arch.trap_state; }
uint64_t get_total_cycles() override { return arch.icount + arch.cycle_offset; }
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; };
bool is_branch_taken() override { return arch.last_branch; };
riscv_hart_msu_vp<BASE> &arch;
};
@ -396,17 +398,24 @@ private:
iss::status read_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 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 write_epc(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 read_fcsr(unsigned addr, reg_t &val);
iss::status write_fcsr(unsigned addr, reg_t val);
virtual iss::status read_custom_csr_reg(unsigned addr, reg_t &val) {return iss::status::Err;};
virtual iss::status write_custom_csr_reg(unsigned addr, reg_t val) {return iss::status::Err;};
void register_custom_csr_rd(unsigned addr){
csr_rd_cb[addr] = &this_class::read_custom_csr_reg;
}
void register_custom_csr_wr(unsigned addr){
csr_wr_cb[addr] = &this_class::write_custom_csr_reg;
}
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:
void check_interrupt();
@ -419,7 +428,7 @@ riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
// reset values
csr[misa] = traits<BASE>::MISA_VAL;
csr[mvendorid] = 0x669;
csr[marchid] = 0x80000003;
csr[marchid] = traits<BASE>::MARCHID_VAL;
csr[mimpid] = 1;
uart_buf.str("");
@ -485,11 +494,11 @@ riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
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_wr_cb[mip] = &this_class::write_ip;
csr_wr_cb[mip] = &this_class::write_null;
csr_rd_cb[sip] = &this_class::read_ip;
csr_wr_cb[sip] = &this_class::write_ip;
csr_wr_cb[sip] = &this_class::write_null;
csr_rd_cb[uip] = &this_class::read_ip;
csr_wr_cb[uip] = &this_class::write_ip;
csr_wr_cb[uip] = &this_class::write_null;
csr_rd_cb[mie] = &this_class::read_ie;
csr_wr_cb[mie] = &this_class::write_ie;
csr_rd_cb[sie] = &this_class::read_ie;
@ -594,13 +603,19 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
try {
switch (space) {
case traits<BASE>::MEM: {
if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) {
auto alignment = is_fetch(access)? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length;
if (unlikely(is_fetch(access) && (addr&(alignment-1)))) {
fault_data = addr;
if (access && iss::access_type::DEBUG) throw trap_access(0, addr);
this->reg.trap_state = (1 << 31); // issue trap 0
this->trap_state = (1 << 31); // issue trap 0
return iss::Err;
}
try {
if(!is_debug(access) && (addr&(alignment-1))){
this->trap_state = 1<<31 | 4<<16;
fault_data=addr;
return iss::Err;
}
if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp);
if (vm.levels != 0) { // VM is active
@ -616,12 +631,12 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data):
read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
if (unlikely(res != iss::Ok)){
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
this->trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
fault_data=addr;
}
return res;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
this->trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
@ -637,7 +652,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
case 3: { // SFENCE:VMA upper
auto tvm = state.mstatus.TVM;
if (this->reg.PRIV == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
@ -658,7 +673,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
}
return iss::Ok;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
this->trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
@ -696,7 +711,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
if (unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) {
fault_data = addr;
if (access && iss::access_type::DEBUG) throw trap_access(0, addr);
this->reg.trap_state = (1 << 31); // issue trap 0
this->trap_state = (1 << 31); // issue trap 0
return iss::Err;
}
try {
@ -715,12 +730,12 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
write_mem(phys_addr_t{access, space, addr}, length, data):
write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
if (unlikely(res != iss::Ok)) {
this->reg.trap_state = (1 << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)
this->trap_state = (1 << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)
fault_data=addr;
}
return res;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
this->trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
@ -769,7 +784,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
ptw.clear();
auto tvm = state.mstatus.TVM;
if (this->reg.PRIV == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
@ -785,7 +800,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
}
return iss::Ok;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
this->trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
@ -830,8 +845,8 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_reg(unsigned
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;
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_cycle(unsigned addr, reg_t &val) {
auto cycle_val = this->icount + cycle_offset;
if (addr == mcycle) {
val = static_cast<reg_t>(cycle_val);
} else if (addr == mcycleh) {
@ -841,7 +856,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_cycle(unsigned a
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cycle(unsigned addr, reg_t val) {
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_cycle(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) {
if (addr == mcycleh)
return iss::Err;
@ -853,11 +868,11 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cycle(unsigned
mcycle_csr = (static_cast<uint64_t>(val)<<32) + (mcycle_csr & 0xffffffff);
}
}
cycle_offset = mcycle_csr-this->reg.icount; // TODO: relying on wrap-around
cycle_offset = mcycle_csr-this->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) {
template <typename BASE> iss::status riscv_hart_msu_vp<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)) {
@ -867,7 +882,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_instret(unsigned
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_instret(unsigned addr, reg_t val) {
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_instret(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) {
if ((addr&0xff) == (minstreth&0xff))
return iss::Err;
@ -883,8 +898,8 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_instret(unsigne
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;
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_time(unsigned addr, reg_t &val) {
uint64_t time_val = this->icount / (100000000 / 32768 - 1); //-> ~3052;
if (addr == time) {
val = static_cast<reg_t>(time_val);
} else if (addr == timeh) {
@ -894,7 +909,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned ad
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_tvec(unsigned addr, reg_t &val) {
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_tvec(unsigned addr, reg_t &val) {
val = csr[addr] & ~2;
return iss::Ok;
}
@ -945,16 +960,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ip(unsigned a
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ip(unsigned addr, reg_t val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
auto mask = get_irq_mask(req_priv_lvl);
mask &= ~(1 << 7); // MTIP is read only
csr[mip] = (csr[mip] & ~mask) | (val & mask);
check_interrupt();
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_epc(unsigned addr, reg_t val) {
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_epc(unsigned addr, reg_t val) {
csr[addr] = val & get_pc_mask();
return iss::Ok;
}
@ -962,7 +968,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_epc(unsigned ad
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned addr, reg_t &val) {
reg_t tvm = state.mstatus.TVM;
if (this->reg.PRIV == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
@ -973,7 +979,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_satp(unsigned addr, reg_t val) {
reg_t tvm = state.mstatus.TVM;
if (this->reg.PRIV == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
@ -1017,7 +1023,6 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_fcsr(unsigne
template <typename BASE>
iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
if(mem_read_cb) return mem_read_cb(paddr, length, data);
switch (paddr.val) {
case 0x0200BFF8: { // CLINT base, mtime reg
if (sizeof(reg_t) < length) return iss::Err;
@ -1029,7 +1034,7 @@ iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length
const mem_type::page_type &p = mem(paddr.val / mem.page_size);
uint64_t offs = paddr.val & mem.page_addr_mask;
std::copy(p.data() + offs, p.data() + offs + length, data);
if (this->reg.icount > 30000) data[3] |= 0x80;
if (this->icount > 30000) data[3] |= 0x80;
} break;
default: {
for(auto offs=0U; offs<length; ++offs) {
@ -1042,7 +1047,6 @@ iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length
template <typename BASE>
iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
if(mem_write_cb) return mem_write_cb(paddr, length, data);
switch (paddr.val) {
case 0x10013000: // UART0 base, TXFIFO reg
case 0x10023000: // UART1 base, TXFIFO reg
@ -1089,7 +1093,7 @@ iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_addr_t paddr, unsigned lengt
LOG(INFO) << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar
<< "), stopping simulation";
}
this->reg.trap_state=std::numeric_limits<uint32_t>::max();
this->trap_state=std::numeric_limits<uint32_t>::max();
this->interrupt_sim=hostvar;
break;
//throw(iss::simulation_stopped(hostvar));
@ -1158,7 +1162,7 @@ template <typename BASE> void riscv_hart_msu_vp<BASE>::check_interrupt() {
if (enabled_interrupts != 0) {
int res = 0;
while ((enabled_interrupts & 1) == 0) enabled_interrupts >>= 1, res++;
this->reg.pending_trap = res << 16 | 1; // 0x80 << 24 | (cause << 16) | trap_id
this->pending_trap = res << 16 | 1; // 0x80 << 24 | (cause << 16) | trap_id
}
}
@ -1276,13 +1280,33 @@ 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
* faulting effective address.
*/
csr[utval | (new_priv << 8)] = cause==2?((instr & 0x3)==3?instr:instr&0xffff):fault_data;
switch(cause){
case 0:
csr[utval | (new_priv << 8)] = static_cast<reg_t>(addr);
break;
case 2:
csr[utval | (new_priv << 8)] = (instr & 0x3)==3?instr:instr&0xffff;
break;
case 3:
//TODO: implement debug mode behavior
// csr[dpc] = addr;
// csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1<<6) | PRIV_M; //FIXME: cause should not be 4 (stepi)
csr[utval | (new_priv << 8)] = addr;
break;
case 4:
case 6:
case 7:
csr[utval | (new_priv << 8)] = fault_data;
break;
default:
csr[utval | (new_priv << 8)] = 0;
}
fault_data = 0;
} else {
if (cur_priv != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)
new_priv = (csr[sideleg] >> cause) & 0x1 ? PRIV_U : PRIV_S;
csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt
this->reg.pending_trap = 0;
this->pending_trap = 0;
}
size_t adr = ucause | (new_priv << 8);
csr[adr] = (trap_id << 31) + cause;
@ -1327,7 +1351,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
<< lvl[cur_priv] << " to " << lvl[new_priv];
// reset trap state
this->reg.PRIV = new_priv;
this->reg.trap_state = 0;
this->trap_state = 0;
update_vm_info();
return this->reg.NEXT_PC;
}
@ -1339,7 +1363,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t f
auto tsr = state.mstatus.TSR;
if (cur_priv == PRIV_S && inst_priv == PRIV_S && tsr != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return this->reg.PC;
}
@ -1378,7 +1402,7 @@ template <typename BASE> void riscv_hart_msu_vp<BASE>::wait_until(uint64_t flags
auto status = state.mstatus;
auto tw = status.TW;
if (this->reg.PRIV == PRIV_S && tw != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
}
}

View File

@ -30,33 +30,33 @@
*
*******************************************************************************/
#include "tgc_c.h"
#include "util/ities.h"
#include <util/logging.h>
#include <iss/arch/tgc_c.h>
#include <cstdio>
#include <cstring>
#include <fstream>
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_aliases;
constexpr std::array<const uint32_t, 40> 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_byte_offsets;
constexpr std::array<const char*, 36> iss::arch::traits<iss::arch::tgc_c>::reg_names;
constexpr std::array<const char*, 36> iss::arch::traits<iss::arch::tgc_c>::reg_aliases;
constexpr std::array<const uint32_t, 43> iss::arch::traits<iss::arch::tgc_c>::reg_bit_widths;
constexpr std::array<const uint32_t, 43> iss::arch::traits<iss::arch::tgc_c>::reg_byte_offsets;
tgc_c::tgc_c() {
reg.icount = 0;
}
tgc_c::tgc_c() = default;
tgc_c::~tgc_c() = default;
void tgc_c::reset(uint64_t address) {
for(size_t i=0; i<traits<tgc_c>::NUM_REGS; ++i) set_reg(i, std::vector<uint8_t>(sizeof(traits<tgc_c>::reg_t),0));
auto base_ptr = reinterpret_cast<traits<tgc_c>::reg_t*>(get_regs_base_ptr());
for(size_t i=0; i<traits<tgc_c>::NUM_REGS; ++i)
*(base_ptr+i)=0;
reg.PC=address;
reg.NEXT_PC=reg.PC;
reg.PRIV=0x3;
reg.trap_state=0;
reg.icount=0;
trap_state=0;
icount=0;
}
uint8_t *tgc_c::get_regs_base_ptr() {

View File

@ -47,23 +47,18 @@ template <> struct traits<tgc_c> {
constexpr static char const* const core_type = "TGC_C";
static constexpr std::array<const char*, 35> reg_names{
{"X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27", "X28", "X29", "X30", "X31", "PC", "NEXT_PC", "PRIV"}};
static constexpr std::array<const char*, 36> reg_names{
{"X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27", "X28", "X29", "X30", "X31", "PC", "NEXT_PC", "PRIV", "DPC"}};
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"}};
static constexpr std::array<const char*, 36> reg_aliases{
{"ZERO", "RA", "SP", "GP", "TP", "T0", "T1", "T2", "S0", "S1", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10", "S11", "T3", "T4", "T5", "T6", "PC", "NEXT_PC", "PRIV", "DPC"}};
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};
enum constants {MISA_VAL=0b01000000000000000001000100000100, MARCHID_VAL=0x80000003, PGMASK=0b111111111111, XLEN=32, INSTR_ALIGNMENT=2, RFS=32, fence=0, fencei=1, fencevmal=2, fencevmau=3, CSR_SIZE=4096, MUL_LEN=64};
constexpr static unsigned FP_REGS_SIZE = 0;
enum reg_e {
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,
PENDING_TRAP,
ICOUNT,
CYCLE,
INSTRET
X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, PC, NEXT_PC, PRIV, DPC, NUM_REGS, TRAP_STATE=NUM_REGS, PENDING_TRAP, ICOUNT, CYCLE, INSTRET, INSTRUCTION, LAST_BRANCH
};
using reg_t = uint32_t;
@ -76,17 +71,17 @@ template <> struct traits<tgc_c> {
using phys_addr_t = iss::typed_addr_t<iss::address_type::PHYSICAL>;
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,64,64}};
static constexpr std::array<const uint32_t, 43> 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,32,64,64,64,32,32}};
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,153,161}};
static constexpr std::array<const uint32_t, 43> 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,149,157,165,173,177}};
static const uint64_t addr_mask = (reg_t(1) << (XLEN - 1)) | ((reg_t(1) << (XLEN - 1)) - 1);
enum sreg_flag_e { FLAGS };
enum mem_type_e { MEM, CSR, FENCE, RES };
enum mem_type_e { MEM, FENCE, RES, CSR };
enum class opcode_e : unsigned short {
LUI = 0,
@ -129,54 +124,53 @@ template <> struct traits<tgc_c> {
FENCE = 37,
ECALL = 38,
EBREAK = 39,
URET = 40,
SRET = 41,
MRET = 42,
WFI = 43,
CSRRW = 44,
CSRRS = 45,
CSRRC = 46,
CSRRWI = 47,
CSRRSI = 48,
CSRRCI = 49,
MUL = 50,
MULH = 51,
MULHSU = 52,
MULHU = 53,
DIV = 54,
DIVU = 55,
REM = 56,
REMU = 57,
CADDI4SPN = 58,
CLW = 59,
CSW = 60,
CADDI = 61,
CNOP = 62,
CJAL = 63,
CLI = 64,
CLUI = 65,
CADDI16SP = 66,
__reserved_clui = 67,
CSRLI = 68,
CSRAI = 69,
CANDI = 70,
CSUB = 71,
CXOR = 72,
COR = 73,
CAND = 74,
CJ = 75,
CBEQZ = 76,
CBNEZ = 77,
CSLLI = 78,
CLWSP = 79,
CMV = 80,
CJR = 81,
__reserved_cmv = 82,
CADD = 83,
CJALR = 84,
CEBREAK = 85,
CSWSP = 86,
DII = 87,
MRET = 40,
WFI = 41,
CSRRW = 42,
CSRRS = 43,
CSRRC = 44,
CSRRWI = 45,
CSRRSI = 46,
CSRRCI = 47,
FENCE_I = 48,
MUL = 49,
MULH = 50,
MULHSU = 51,
MULHU = 52,
DIV = 53,
DIVU = 54,
REM = 55,
REMU = 56,
CADDI4SPN = 57,
CLW = 58,
CSW = 59,
CADDI = 60,
CNOP = 61,
CJAL = 62,
CLI = 63,
CLUI = 64,
CADDI16SP = 65,
__reserved_clui = 66,
CSRLI = 67,
CSRAI = 68,
CANDI = 69,
CSUB = 70,
CXOR = 71,
COR = 72,
CAND = 73,
CJ = 74,
CBEQZ = 75,
CBNEZ = 76,
CSLLI = 77,
CLWSP = 78,
CMV = 79,
CJR = 80,
__reserved_cmv = 81,
CADD = 82,
CJALR = 83,
CEBREAK = 84,
CSWSP = 85,
DII = 86,
MAX_OPCODE
};
};
@ -194,16 +188,8 @@ struct tgc_c: public arch_if {
void reset(uint64_t address=0) override;
uint8_t* get_regs_base_ptr() override;
/// deprecated
void get_reg(short idx, std::vector<uint8_t>& value) override {}
void set_reg(short idx, const std::vector<uint8_t>& value) override {}
/// deprecated
bool get_flag(int flag) override {return false;}
void set_flag(int, bool value) override {};
/// deprecated
void update_flags(operations op, uint64_t opr1, uint64_t opr2) override {};
inline uint64_t get_icount() { return reg.icount; }
inline uint64_t get_icount() { return icount; }
inline bool should_stop() { return interrupt_sim; }
@ -221,9 +207,9 @@ struct tgc_c: public arch_if {
virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; }
inline uint32_t get_last_branch() { return reg.last_branch; }
inline uint32_t get_last_branch() { return last_branch; }
protected:
#pragma pack(push, 1)
struct TGC_C_regs {
uint32_t X0 = 0;
@ -261,13 +247,21 @@ protected:
uint32_t PC = 0;
uint32_t NEXT_PC = 0;
uint8_t PRIV = 0;
uint32_t DPC = 0;
uint32_t trap_state = 0, pending_trap = 0;
uint64_t icount = 0;
uint64_t cycle = 0;
uint64_t instret = 0;
uint32_t last_branch;
uint32_t instruction = 0;
uint32_t last_branch = 0;
} reg;
#pragma pack(pop)
uint32_t trap_state = 0, pending_trap = 0;
uint64_t icount = 0;
uint64_t cycle = 0;
uint64_t instret = 0;
uint32_t instruction = 0;
uint32_t last_branch = 0;
std::array<address_type, 4> addr_mode;
uint64_t interrupt_sim=0;

View File

@ -0,0 +1,175 @@
#include "tgc_c.h"
#include <vector>
#include <array>
#include <cstdlib>
#include <algorithm>
namespace iss {
namespace arch {
namespace {
// according to
// https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation
#ifdef __GCC__
constexpr size_t bit_count(uint32_t u) { return __builtin_popcount(u); }
#elif __cplusplus < 201402L
constexpr size_t uCount(uint32_t u) { return u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); }
constexpr size_t bit_count(uint32_t u) { return ((uCount(u) + (uCount(u) >> 3)) & 030707070707) % 63; }
#else
constexpr size_t bit_count(uint32_t u) {
size_t uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
return ((uCount + (uCount >> 3)) & 030707070707) % 63;
}
#endif
using opcode_e = traits<tgc_c>::opcode_e;
/****************************************************************************
* start opcode definitions
****************************************************************************/
struct instruction_desriptor {
size_t length;
uint32_t value;
uint32_t mask;
opcode_e op;
};
const std::array<instruction_desriptor, 90> instr_descr = {{
/* entries are: size, valid value, valid mask, function ptr */
{32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, opcode_e::LUI},
{32, 0b00000000000000000000000000010111, 0b00000000000000000000000001111111, opcode_e::AUIPC},
{32, 0b00000000000000000000000001101111, 0b00000000000000000000000001111111, opcode_e::JAL},
{32, 0b00000000000000000000000001100111, 0b00000000000000000111000001111111, opcode_e::JALR},
{32, 0b00000000000000000000000001100011, 0b00000000000000000111000001111111, opcode_e::BEQ},
{32, 0b00000000000000000001000001100011, 0b00000000000000000111000001111111, opcode_e::BNE},
{32, 0b00000000000000000100000001100011, 0b00000000000000000111000001111111, opcode_e::BLT},
{32, 0b00000000000000000101000001100011, 0b00000000000000000111000001111111, opcode_e::BGE},
{32, 0b00000000000000000110000001100011, 0b00000000000000000111000001111111, opcode_e::BLTU},
{32, 0b00000000000000000111000001100011, 0b00000000000000000111000001111111, opcode_e::BGEU},
{32, 0b00000000000000000000000000000011, 0b00000000000000000111000001111111, opcode_e::LB},
{32, 0b00000000000000000001000000000011, 0b00000000000000000111000001111111, opcode_e::LH},
{32, 0b00000000000000000010000000000011, 0b00000000000000000111000001111111, opcode_e::LW},
{32, 0b00000000000000000100000000000011, 0b00000000000000000111000001111111, opcode_e::LBU},
{32, 0b00000000000000000101000000000011, 0b00000000000000000111000001111111, opcode_e::LHU},
{32, 0b00000000000000000000000000100011, 0b00000000000000000111000001111111, opcode_e::SB},
{32, 0b00000000000000000001000000100011, 0b00000000000000000111000001111111, opcode_e::SH},
{32, 0b00000000000000000010000000100011, 0b00000000000000000111000001111111, opcode_e::SW},
{32, 0b00000000000000000000000000010011, 0b00000000000000000111000001111111, opcode_e::ADDI},
{32, 0b00000000000000000010000000010011, 0b00000000000000000111000001111111, opcode_e::SLTI},
{32, 0b00000000000000000011000000010011, 0b00000000000000000111000001111111, opcode_e::SLTIU},
{32, 0b00000000000000000100000000010011, 0b00000000000000000111000001111111, opcode_e::XORI},
{32, 0b00000000000000000110000000010011, 0b00000000000000000111000001111111, opcode_e::ORI},
{32, 0b00000000000000000111000000010011, 0b00000000000000000111000001111111, opcode_e::ANDI},
{32, 0b00000000000000000001000000010011, 0b11111110000000000111000001111111, opcode_e::SLLI},
{32, 0b00000000000000000101000000010011, 0b11111110000000000111000001111111, opcode_e::SRLI},
{32, 0b01000000000000000101000000010011, 0b11111110000000000111000001111111, opcode_e::SRAI},
{32, 0b00000000000000000000000000110011, 0b11111110000000000111000001111111, opcode_e::ADD},
{32, 0b01000000000000000000000000110011, 0b11111110000000000111000001111111, opcode_e::SUB},
{32, 0b00000000000000000001000000110011, 0b11111110000000000111000001111111, opcode_e::SLL},
{32, 0b00000000000000000010000000110011, 0b11111110000000000111000001111111, opcode_e::SLT},
{32, 0b00000000000000000011000000110011, 0b11111110000000000111000001111111, opcode_e::SLTU},
{32, 0b00000000000000000100000000110011, 0b11111110000000000111000001111111, opcode_e::XOR},
{32, 0b00000000000000000101000000110011, 0b11111110000000000111000001111111, opcode_e::SRL},
{32, 0b01000000000000000101000000110011, 0b11111110000000000111000001111111, opcode_e::SRA},
{32, 0b00000000000000000110000000110011, 0b11111110000000000111000001111111, opcode_e::OR},
{32, 0b00000000000000000111000000110011, 0b11111110000000000111000001111111, opcode_e::AND},
{32, 0b00000000000000000000000000001111, 0b00000000000000000111000001111111, opcode_e::FENCE},
{32, 0b00000000000000000000000001110011, 0b11111111111111111111111111111111, opcode_e::ECALL},
{32, 0b00000000000100000000000001110011, 0b11111111111111111111111111111111, opcode_e::EBREAK},
{32, 0b00000000001000000000000001110011, 0b11111111111111111111111111111111, opcode_e::URET},
{32, 0b00010000001000000000000001110011, 0b11111111111111111111111111111111, opcode_e::SRET},
{32, 0b00110000001000000000000001110011, 0b11111111111111111111111111111111, opcode_e::MRET},
{32, 0b00010000010100000000000001110011, 0b11111111111111111111111111111111, opcode_e::WFI},
{32, 0b01111011001000000000000001110011, 0b11111111111111111111111111111111, opcode_e::DRET},
{32, 0b00000000000000000001000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRW},
{32, 0b00000000000000000010000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRS},
{32, 0b00000000000000000011000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRC},
{32, 0b00000000000000000101000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRWI},
{32, 0b00000000000000000110000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRSI},
{32, 0b00000000000000000111000001110011, 0b00000000000000000111000001111111, opcode_e::CSRRCI},
{32, 0b00000000000000000001000000001111, 0b00000000000000000111000001111111, opcode_e::FENCE_I},
{32, 0b00000010000000000000000000110011, 0b11111110000000000111000001111111, opcode_e::MUL},
{32, 0b00000010000000000001000000110011, 0b11111110000000000111000001111111, opcode_e::MULH},
{32, 0b00000010000000000010000000110011, 0b11111110000000000111000001111111, opcode_e::MULHSU},
{32, 0b00000010000000000011000000110011, 0b11111110000000000111000001111111, opcode_e::MULHU},
{32, 0b00000010000000000100000000110011, 0b11111110000000000111000001111111, opcode_e::DIV},
{32, 0b00000010000000000101000000110011, 0b11111110000000000111000001111111, opcode_e::DIVU},
{32, 0b00000010000000000110000000110011, 0b11111110000000000111000001111111, opcode_e::REM},
{32, 0b00000010000000000111000000110011, 0b11111110000000000111000001111111, opcode_e::REMU},
{16, 0b0000000000000000, 0b1110000000000011, opcode_e::CADDI4SPN},
{16, 0b0100000000000000, 0b1110000000000011, opcode_e::CLW},
{16, 0b1100000000000000, 0b1110000000000011, opcode_e::CSW},
{16, 0b0000000000000001, 0b1110000000000011, opcode_e::CADDI},
{16, 0b0000000000000001, 0b1110111110000011, opcode_e::CNOP},
{16, 0b0010000000000001, 0b1110000000000011, opcode_e::CJAL},
{16, 0b0100000000000001, 0b1110000000000011, opcode_e::CLI},
{16, 0b0110000000000001, 0b1110000000000011, opcode_e::CLUI},
{16, 0b0110000100000001, 0b1110111110000011, opcode_e::CADDI16SP},
{16, 0b0110000000000001, 0b1111000001111111, opcode_e::__reserved_clui},
{16, 0b1000000000000001, 0b1111110000000011, opcode_e::CSRLI},
{16, 0b1000010000000001, 0b1111110000000011, opcode_e::CSRAI},
{16, 0b1000100000000001, 0b1110110000000011, opcode_e::CANDI},
{16, 0b1000110000000001, 0b1111110001100011, opcode_e::CSUB},
{16, 0b1000110000100001, 0b1111110001100011, opcode_e::CXOR},
{16, 0b1000110001000001, 0b1111110001100011, opcode_e::COR},
{16, 0b1000110001100001, 0b1111110001100011, opcode_e::CAND},
{16, 0b1010000000000001, 0b1110000000000011, opcode_e::CJ},
{16, 0b1100000000000001, 0b1110000000000011, opcode_e::CBEQZ},
{16, 0b1110000000000001, 0b1110000000000011, opcode_e::CBNEZ},
{16, 0b0000000000000010, 0b1111000000000011, opcode_e::CSLLI},
{16, 0b0100000000000010, 0b1110000000000011, opcode_e::CLWSP},
{16, 0b1000000000000010, 0b1111000000000011, opcode_e::CMV},
{16, 0b1000000000000010, 0b1111000001111111, opcode_e::CJR},
{16, 0b1000000000000010, 0b1111111111111111, opcode_e::__reserved_cmv},
{16, 0b1001000000000010, 0b1111000000000011, opcode_e::CADD},
{16, 0b1001000000000010, 0b1111000001111111, opcode_e::CJALR},
{16, 0b1001000000000010, 0b1111111111111111, opcode_e::CEBREAK},
{16, 0b1100000000000010, 0b1110000000000011, opcode_e::CSWSP},
{16, 0b0000000000000000, 0b1111111111111111, opcode_e::DII},
}};
}
template<>
struct instruction_decoder<tgc_c> {
using opcode_e = traits<tgc_c>::opcode_e;
using code_word_t=traits<tgc_c>::code_word_t;
struct instruction_pattern {
uint32_t value;
uint32_t mask;
opcode_e id;
};
std::array<std::vector<instruction_pattern>, 4> qlut;
template<typename T>
unsigned decode_instruction(T);
instruction_decoder() {
for (auto instr : instr_descr) {
auto quadrant = instr.value & 0x3;
qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
}
for(auto& lut: qlut){
std::sort(std::begin(lut), std::end(lut), [](instruction_pattern const& a, instruction_pattern const& b){
return bit_count(a.mask) > bit_count(b.mask);
});
}
}
};
template<>
unsigned instruction_decoder<tgc_c>::decode_instruction<traits<tgc_c>::code_word_t>(traits<tgc_c>::code_word_t instr){
auto res = std::find_if(std::begin(qlut[instr&0x3]), std::end(qlut[instr&0x3]), [instr](instruction_pattern const& e){
return !((instr&e.mask) ^ e.value );
});
return static_cast<unsigned>(res!=std::end(qlut[instr&0x3])? res->id : opcode_e::MAX_OPCODE);
}
std::unique_ptr<instruction_decoder<tgc_c>> traits<tgc_c>::get_decoder(){
return std::make_unique<instruction_decoder<tgc_c>>();
}
}
}

50
src/iss/arch/tgc_mapper.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef _ISS_ARCH_TGC_MAPPER_H
#define _ISS_ARCH_TGC_MAPPER_H
#include "riscv_hart_m_p.h"
#include "tgc_c.h"
using tgc_c_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_c>;
#ifdef CORE_TGC_A
#include "riscv_hart_m_p.h"
#include <iss/arch/tgc_a.h>
using tgc_a_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_a>;
#endif
#ifdef CORE_TGC_B
#include "riscv_hart_m_p.h"
#include <iss/arch/tgc_b.h>
using tgc_b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_b>;
#endif
#ifdef CORE_TGC_C_XRB_NN
#include "riscv_hart_m_p.h"
#include "hwl.h"
#include <iss/arch/tgc_c_xrb_nn.h>
using tgc_c_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_m_p<iss::arch::tgc_c_xrb_nn>>;
#endif
#ifdef CORE_TGC_D
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc_d.h>
using tgc_d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC_D_XRB_MAC
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc_d_xrb_mac.h>
using tgc_d_xrb_mac_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d_xrb_mac, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC_D_XRB_NN
#include "riscv_hart_mu_p.h"
#include "hwl.h"
#include <iss/arch/tgc_d_xrb_nn.h>
using tgc_d_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc_d_xrb_nn, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>>;
#endif
#ifdef CORE_TGC_E
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc_e.h>
using tgc_e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_e, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC_X
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc_x.h>
using tgc_x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_x, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N | iss::arch::FEAT_TCM)>;
#endif
#endif

172
src/iss/arch/wt_cache.h Normal file
View File

@ -0,0 +1,172 @@
/*******************************************************************************
* Copyright (C) 2023 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial implementation
******************************************************************************/
#ifndef _RISCV_HART_M_P_WT_CACHE_H
#define _RISCV_HART_M_P_WT_CACHE_H
#include <iss/vm_types.h>
#include <util/ities.h>
#include <vector>
#include <map>
#include <memory>
namespace iss {
namespace arch {
namespace cache {
enum class state { INVALID, VALID};
struct line {
uint64_t tag_addr{0};
state st{state::INVALID};
std::vector<uint8_t> data;
line(unsigned line_sz): data(line_sz) {}
};
struct set {
std::vector<line> ways;
set(unsigned ways_count, line const& l): ways(ways_count, l) {}
};
struct cache {
std::vector<set> sets;
cache(unsigned size, unsigned line_sz, unsigned ways) {
line const ref_line{line_sz};
set const ref_set{ways, ref_line};
sets.resize(size/(ways*line_sz), ref_set);
}
};
struct wt_policy {
bool is_cacheline_hit(cache& c );
};
}
// write thru, allocate on read, direct mapped or set-associative with round-robin replacement policy
template <typename BASE> class wt_cache : public BASE {
public:
using base_class = BASE;
using this_class = wt_cache<BASE>;
using reg_t = typename BASE::reg_t;
using mem_read_f = typename BASE::mem_read_f;
using mem_write_f = typename BASE::mem_write_f;
using phys_addr_t = typename BASE::phys_addr_t;
wt_cache();
virtual ~wt_cache() = default;
unsigned size{4096};
unsigned line_sz{32};
unsigned ways{1};
uint64_t io_address{0xf0000000};
uint64_t io_addr_mask{0xf0000000};
protected:
iss::status read_cache(phys_addr_t addr, unsigned, uint8_t *const);
iss::status write_cache(phys_addr_t addr, unsigned, uint8_t const *const);
std::function<mem_read_f> cache_mem_rd_delegate;
std::function<mem_write_f> cache_mem_wr_delegate;
std::unique_ptr<cache::cache> dcache_ptr;
std::unique_ptr<cache::cache> icache_ptr;
size_t get_way_select() {
return 0;
}
};
template<typename BASE>
inline wt_cache<BASE>::wt_cache() {
auto cb = base_class::replace_mem_access(
[this](phys_addr_t a, unsigned l, uint8_t* const d) -> iss::status { return read_cache(a, l,d);},
[this](phys_addr_t a, unsigned l, uint8_t const* const d) -> iss::status { return write_cache(a, l,d);});
cache_mem_rd_delegate = cb.first;
cache_mem_wr_delegate = cb.second;
}
template<typename BASE>
iss::status iss::arch::wt_cache<BASE>::read_cache(phys_addr_t a, unsigned l, uint8_t* const d) {
if(!icache_ptr) {
icache_ptr.reset(new cache::cache(size, line_sz, ways));
dcache_ptr.reset(new cache::cache(size, line_sz, ways));
}
if((a.val&io_addr_mask) != io_address) {
auto set_addr=(a.val&(size-1))>>util::ilog2(line_sz*ways);
auto tag_addr=a.val>>util::ilog2(line_sz);
auto& set = (is_fetch(a.access)?icache_ptr:dcache_ptr)->sets[set_addr];
for(auto& cl: set.ways) {
if(cl.st==cache::state::VALID && cl.tag_addr==tag_addr) {
auto start_addr = a.val&(line_sz-1);
for(auto i = 0U; i<l; ++i)
d[i] = cl.data[start_addr+i];
return iss::Ok;
}
}
auto& cl = set.ways[get_way_select()];
phys_addr_t cl_addr{a};
cl_addr.val=tag_addr<<util::ilog2(line_sz);
cache_mem_rd_delegate(cl_addr, line_sz, cl.data.data());
cl.tag_addr=tag_addr;
cl.st=cache::state::VALID;
auto start_addr = a.val&(line_sz-1);
for(auto i = 0U; i<l; ++i)
d[i] = cl.data[start_addr+i];
return iss::Ok;
} else
return cache_mem_rd_delegate(a, l, d);
}
template<typename BASE>
iss::status iss::arch::wt_cache<BASE>::write_cache(phys_addr_t a, unsigned l, const uint8_t* const d) {
if(!dcache_ptr)
dcache_ptr.reset(new cache::cache(size, line_sz, ways));
auto res = cache_mem_wr_delegate(a, l, d);
if(res == iss::Ok && ((a.val&io_addr_mask) != io_address)) {
auto set_addr=(a.val&(size-1))>>util::ilog2(line_sz*ways);
auto tag_addr=a.val>>util::ilog2(line_sz);
auto& set = dcache_ptr->sets[set_addr];
for(auto& cl: set.ways) {
if(cl.st==cache::state::VALID && cl.tag_addr==tag_addr) {
auto start_addr = a.val&(line_sz-1);
for(auto i = 0U; i<l; ++i)
cl.data[start_addr+i] = d[i];
break;
}
}
}
return res;
}
} // namespace arch
} // namespace iss
#endif /* _RISCV_HART_M_P_H */

View File

@ -120,9 +120,9 @@ public:
status packetsize_query(std::string &out_buf) override;
status add_break(int type, uint64_t addr, unsigned int length) override;
status add_break(break_type type, uint64_t addr, unsigned int length) override;
status remove_break(int type, uint64_t addr, unsigned int length) override;
status remove_break(break_type type, uint64_t addr, unsigned int length) override;
status resume_from_addr(bool step, int sig, uint64_t addr, rp_thread_ref thread,
std::function<void(unsigned)> stop_callback) override;
@ -214,13 +214,27 @@ template <typename ARCH> status riscv_target_adapter<ARCH>::write_registers(cons
auto start_reg=arch::traits<ARCH>::X0;
auto *reg_base = core->get_regs_base_ptr();
auto iter = data.data();
bool e_ext = arch::traits<ARCH>::PC<32;
for (size_t reg_no = 0; reg_no < start_reg+33/*arch::traits<ARCH>::NUM_REGS*/; ++reg_no) {
if(e_ext && reg_no>15){
if(reg_no==32){
auto reg_width = arch::traits<ARCH>::reg_bit_widths[arch::traits<ARCH>::PC] / 8;
auto offset = traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC];
std::copy(iter, iter + reg_width, reg_base);
} else {
const uint64_t zero_val=0;
auto reg_width = arch::traits<ARCH>::reg_bit_widths[15] / 8;
auto iter = (uint8_t*)&zero_val;
std::copy(iter, iter + reg_width, reg_base);
}
} else {
auto reg_width = arch::traits<ARCH>::reg_bit_widths[reg_no] / 8;
auto offset = traits<ARCH>::reg_byte_offsets[reg_no];
std::copy(iter, iter + reg_width, reg_base);
iter += 4;
reg_base += offset;
}
}
return Ok;
}
@ -317,7 +331,12 @@ template <typename ARCH> status riscv_target_adapter<ARCH>::packetsize_query(std
return Ok;
}
template <typename ARCH> status riscv_target_adapter<ARCH>::add_break(int type, uint64_t addr, unsigned int length) {
template <typename ARCH> status riscv_target_adapter<ARCH>::add_break(break_type type, uint64_t addr, unsigned int length) {
switch(type) {
default:
return Err;
case SW_EXEC:
case HW_EXEC: {
auto saddr = map_addr({iss::access_type::FETCH, iss::address_type::PHYSICAL, 0, addr});
auto eaddr = map_addr({iss::access_type::FETCH, iss::address_type::PHYSICAL, 0, addr + length});
target_adapter_base::bp_lut.addEntry(++target_adapter_base::bp_count, saddr.val, eaddr.val - saddr.val);
@ -326,8 +345,15 @@ template <typename ARCH> status riscv_target_adapter<ARCH>::add_break(int type,
LOG(TRACE) << "Now having " << target_adapter_base::bp_lut.size() << " breakpoints";
return Ok;
}
}
}
template <typename ARCH> status riscv_target_adapter<ARCH>::remove_break(int type, uint64_t addr, unsigned int length) {
template <typename ARCH> status riscv_target_adapter<ARCH>::remove_break(break_type type, uint64_t addr, unsigned int length) {
switch(type) {
default:
return Err;
case SW_EXEC:
case HW_EXEC: {
auto saddr = map_addr({iss::access_type::FETCH, iss::address_type::PHYSICAL, 0, addr});
unsigned handle = target_adapter_base::bp_lut.getEntry(saddr.val);
if (handle) {
@ -341,6 +367,8 @@ template <typename ARCH> status riscv_target_adapter<ARCH>::remove_break(int typ
LOG(TRACE) << "Now having " << target_adapter_base::bp_lut.size() << " breakpoints";
return Err;
}
}
}
template <typename ARCH>
status riscv_target_adapter<ARCH>::resume_from_addr(bool step, int sig, uint64_t addr, rp_thread_ref thread,

View File

@ -50,7 +50,7 @@ std::tuple<cpu_ptr, vm_ptr> create_cpu(std::string const& backend, unsigned gdb_
if(backend == "llvm")
return {cpu_ptr{lcpu}, vm_ptr{iss::llvm::create(lcpu, gdb_port)}};
#endif
#ifdef WITH_LLVM
#ifdef WITH_TCC
if(backend == "tcc")
return {cpu_ptr{lcpu}, vm_ptr{iss::tcc::create(lcpu, gdb_port)}};
#endif

View File

@ -0,0 +1,118 @@
/*******************************************************************************
* Copyright (C) 2017 - 2023, MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial API and implementation
******************************************************************************/
#include "cycle_estimate.h"
#include <iss/arch_if.h>
#include <util/logging.h>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/error/en.h>
#include <fstream>
using namespace rapidjson;
using namespace std;
iss::plugin::cycle_estimate::cycle_estimate(string const& config_file_name)
: instr_if(nullptr)
, config_file_name(config_file_name)
{
}
iss::plugin::cycle_estimate::~cycle_estimate() {
}
bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if& vm) {
instr_if = vm.get_arch()->get_instrumentation_if();
if(!instr_if) return false;
const string core_name = instr_if->core_type_name();
if (config_file_name.length() > 0) {
ifstream is(config_file_name);
if (is.is_open()) {
try {
IStreamWrapper isw(is);
Document d;
ParseResult ok = d.ParseStream(isw);
if(ok) {
Value& val = d[core_name.c_str()];
if(val.IsArray()){
delays.reserve(val.Size());
for (auto it = val.Begin(); it != val.End(); ++it) {
auto& name = (*it)["name"];
auto& size = (*it)["size"];
auto& delay = (*it)["delay"];
auto& branch = (*it)["branch"];
if(delay.IsArray()) {
auto dt = delay[0].Get<unsigned>();
auto dnt = delay[1].Get<unsigned>();
delays.push_back(instr_desc{size.Get<unsigned>(), dt, dnt, branch.Get<bool>()});
} else if(delay.Is<unsigned>()) {
auto d = delay.Get<unsigned>();
delays.push_back(instr_desc{size.Get<unsigned>(), d, d, branch.Get<bool>()});
} else
throw runtime_error("JSON parse error");
}
} else {
LOG(ERR)<<"plugin cycle_estimate: could not find an entry for "<<core_name<<" in JSON file"<<endl;
return false;
}
} else {
LOG(ERR)<<"plugin cycle_estimate: could not parse in JSON file at "<< ok.Offset()<<": "<<GetParseError_En(ok.Code())<<endl;
return false;
}
} catch (runtime_error &e) {
LOG(ERR) << "Could not parse input file " << config_file_name << ", reason: " << e.what();
return false;
}
} else {
LOG(ERR) << "Could not open input file " << config_file_name;
return false;
}
}
return true;
}
void iss::plugin::cycle_estimate::callback(instr_info_t instr_info) {
assert(instr_if && "No instrumentation interface available but callback executed");
auto entry = delays[instr_info.instr_id];
bool taken = instr_if->is_branch_taken();
if (taken && (entry.taken > 1))
instr_if->update_last_instr_cycles(entry.taken);
else if (entry.not_taken > 1)
instr_if->update_last_instr_cycles(entry.not_taken);
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2017, 2018, MINRES Technologies GmbH
* Copyright (C) 2017 - 2023, MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,23 +37,25 @@
#include "iss/instrumentation_if.h"
#include "iss/vm_plugin.h"
#include <json/json.h>
#include <string>
#include <unordered_map>
#include <vector>
namespace iss {
namespace plugin {
class cycle_estimate: public iss::vm_plugin {
class cycle_estimate: public vm_plugin {
BEGIN_BF_DECL(instr_desc, uint32_t)
BF_FIELD(taken, 24, 8)
BF_FIELD(not_taken, 16, 8)
BF_FIELD(size, 0, 16)
instr_desc(uint32_t size, uint32_t taken, uint32_t not_taken): instr_desc() {
BF_FIELD(is_branch, 8, 8)
BF_FIELD(size, 0, 8)
instr_desc(uint32_t size, uint32_t taken, uint32_t not_taken, bool branch): instr_desc() {
this->size=size;
this->taken=taken;
this->not_taken=not_taken;
this->is_branch=branch;
}
END_BF_DECL();
@ -64,7 +66,7 @@ public:
cycle_estimate(const cycle_estimate &&) = delete;
cycle_estimate(std::string config_file_name);
cycle_estimate(std::string const& config_file_name);
virtual ~cycle_estimate();
@ -76,10 +78,10 @@ public:
sync_type get_sync() override { return POST_SYNC; };
void callback(instr_info_t instr_info, exec_info const&) override;
void callback(instr_info_t instr_info) override;
private:
iss::instrumentation_if *arch_instr;
iss::instrumentation_if *instr_if;
std::vector<instr_desc> delays;
struct pair_hash {
size_t operator()(const std::pair<uint64_t, uint64_t> &p) const {
@ -88,7 +90,7 @@ private:
}
};
std::unordered_map<std::pair<uint64_t, uint64_t>, uint64_t, pair_hash> blocks;
Json::Value root;
std::string config_file_name;
};
}
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2017, MINRES Technologies GmbH
* Copyright (C) 2017 - 2023 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -32,8 +32,8 @@
* eyck@minres.com - initial API and implementation
******************************************************************************/
#include "iss/plugin/instruction_count.h"
#include "iss/instrumentation_if.h"
#include "instruction_count.h"
#include <iss/instrumentation_if.h>
#include <iss/arch_if.h>
#include <util/logging.h>
@ -90,6 +90,6 @@ bool iss::plugin::instruction_count::registration(const char* const version, vm_
return true;
}
void iss::plugin::instruction_count::callback(instr_info_t instr_info, exec_info const&) {
void iss::plugin::instruction_count::callback(instr_info_t instr_info) {
rep_counts[instr_info.instr_id]++;
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2017, 2018, MINRES Technologies GmbH
* Copyright (C) 2017 - 2023, MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -69,7 +69,7 @@ public:
sync_type get_sync() override { return POST_SYNC; };
void callback(instr_info_t, exec_info const&) override;
void callback(instr_info_t) override;
private:
Json::Value root;

214
src/iss/plugin/pctrace.cpp Normal file
View File

@ -0,0 +1,214 @@
/*******************************************************************************
* Copyright (C) 2017 - 2023, MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* alex.com - initial implementation
******************************************************************************/
#include <iss/arch_if.h>
#include <iss/plugin/pctrace.h>
#include <util/logging.h>
#include <util/ities.h>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/error/en.h>
#include <fstream>
#include <iostream>
#ifdef WITH_LZ4
#include <lz4frame.h>
#endif
namespace iss {
namespace plugin {
using namespace rapidjson;
using namespace std;
#ifdef WITH_LZ4
class lz4compress_steambuf: public std::streambuf {
public:
lz4compress_steambuf(const lz4compress_steambuf&) = delete;
lz4compress_steambuf& operator=(const lz4compress_steambuf&) = delete;
lz4compress_steambuf(std::ostream &sink, size_t buf_size)
: sink(sink)
, src_buf(buf_size)
, dest_buf(LZ4F_compressBound(buf_size, nullptr))
{
auto errCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
if (LZ4F_isError(errCode) != 0)
throw std::runtime_error(std::string("Failed to create LZ4 context: ") + LZ4F_getErrorName(errCode));
size_t ret = LZ4F_compressBegin(ctx, &dest_buf.front(), dest_buf.capacity(), nullptr);
if (LZ4F_isError(ret) != 0)
throw std::runtime_error(std::string("Failed to start LZ4 compression: ") + LZ4F_getErrorName(ret));
setp(src_buf.data(), src_buf.data() + src_buf.size() - 1);
sink.write(dest_buf.data(), ret);
}
~lz4compress_steambuf() {
close();
}
void close() {
if (closed)
return;
sync();
auto ret = LZ4F_compressEnd(ctx, dest_buf.data(), dest_buf.capacity(), nullptr);
if (LZ4F_isError(ret) != 0)
throw std::runtime_error(std::string("Failed to finish LZ4 compression: ") + LZ4F_getErrorName(ret));
sink.write(dest_buf.data(), ret);
LZ4F_freeCompressionContext(ctx);
closed = true;
}
private:
int_type overflow(int_type ch) override {
compress_and_write();
*pptr() = static_cast<char_type>(ch);
pbump(1);
return ch;
}
int_type sync() override {
compress_and_write();
return 0;
}
void compress_and_write() {
if (closed)
throw std::runtime_error("Cannot write to closed stream");
if(auto orig_size = pptr() - pbase()){
auto ret = LZ4F_compressUpdate(ctx, dest_buf.data(), dest_buf.capacity(), pbase(), orig_size, nullptr);
if (LZ4F_isError(ret) != 0)
throw std::runtime_error(std::string("LZ4 compression failed: ") + LZ4F_getErrorName(ret));
if(ret) sink.write(dest_buf.data(), ret);
pbump(-orig_size);
}
}
std::ostream &sink;
std::vector<char> src_buf;
std::vector<char> dest_buf;
LZ4F_compressionContext_t ctx{ nullptr };
bool closed{ false };
};
#endif
pctrace::pctrace(std::string const &filename)
: instr_if(nullptr)
, filename(filename)
, output("output.trc")
#ifdef WITH_LZ4
, strbuf(new lz4compress_steambuf(output, 4096))
, ostr(strbuf.get())
#endif
{ }
pctrace::~pctrace() { }
bool pctrace::registration(const char *const version, vm_if& vm) {
instr_if = vm.get_arch()->get_instrumentation_if();
if(!instr_if) return false;
const string core_name = instr_if->core_type_name();
if (filename.length() > 0) {
ifstream is(filename);
if (is.is_open()) {
try {
IStreamWrapper isw(is);
Document d;
ParseResult ok = d.ParseStream(isw);
if(ok) {
Value& val = d[core_name.c_str()];
if(val.IsArray()){
delays.reserve(val.Size());
for (auto it = val.Begin(); it != val.End(); ++it) {
auto& name = (*it)["name"];
auto& size = (*it)["size"];
auto& delay = (*it)["delay"];
auto& branch = (*it)["branch"];
if(delay.IsArray()) {
auto dt = delay[0].Get<unsigned>();
auto dnt = delay[1].Get<unsigned>();
delays.push_back(instr_desc{size.Get<unsigned>(), dt, dnt, branch.Get<bool>()});
} else if(delay.Is<unsigned>()) {
auto d = delay.Get<unsigned>();
delays.push_back(instr_desc{size.Get<unsigned>(), d, d, branch.Get<bool>()});
} else
throw runtime_error("JSON parse error");
}
} else {
LOG(ERR)<<"plugin cycle_estimate: could not find an entry for "<<core_name<<" in JSON file"<<endl;
return false;
}
} else {
LOG(ERR)<<"plugin cycle_estimate: could not parse in JSON file at "<< ok.Offset()<<": "<<GetParseError_En(ok.Code())<<endl;
return false;
}
} catch (runtime_error &e) {
LOG(ERR) << "Could not parse input file " << filename << ", reason: " << e.what();
return false;
}
} else {
LOG(ERR) << "Could not open input file " << filename;
return false;
}
}
return true;
}
void pctrace::callback(instr_info_t iinfo) {
auto delay = 0;
size_t id = iinfo.instr_id;
auto entry = delays[id];
auto instr = instr_if->get_instr_word();
auto call = id==65 || id ==86 || ((id==2 || id==3) && bit_sub<7,5>(instr)!=0) ;//not taking care of tail calls (jalr with loading x6)
bool taken = instr_if->is_branch_taken();
bool compressed = (instr&0x3)!=0x3;
if (taken) {
delay = entry.taken;
if(entry.taken > 1)
instr_if->update_last_instr_cycles(entry.taken);
} else {
delay = entry.not_taken;
if (entry.not_taken > 1)
instr_if->update_last_instr_cycles(entry.not_taken);
}
#ifndef WITH_LZ4
output<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay <<"," << call<<","<<(compressed?2:4) <<"\n";
#else
auto rdbuf=ostr.rdbuf();
ostr<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay <<"," << call<<","<<(compressed?2:4) <<"\n";
#endif
}
}
}

102
src/iss/plugin/pctrace.h Normal file
View File

@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (C) 2017 - 2023, MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial API and implementation
******************************************************************************/
#ifndef _ISS_PLUGIN_COV_H_
#define _ISS_PLUGIN_COV_H_
#include <iss/vm_plugin.h>
#include "iss/instrumentation_if.h"
#include <json/json.h>
#include <string>
#include <fstream>
namespace iss {
namespace plugin {
class lz4compress_steambuf;
class pctrace : public iss::vm_plugin {
struct instr_delay {
std::string instr_name;
size_t size;
size_t not_taken_delay;
size_t taken_delay;
};
BEGIN_BF_DECL(instr_desc, uint32_t)
BF_FIELD(taken, 24, 8)
BF_FIELD(not_taken, 16, 8)
BF_FIELD(is_branch, 8, 8)
BF_FIELD(size, 0, 8)
instr_desc(uint32_t size, uint32_t taken, uint32_t not_taken, bool branch): instr_desc() {
this->size=size;
this->taken=taken;
this->not_taken=not_taken;
this->is_branch=branch;
}
END_BF_DECL();
public:
pctrace(const pctrace &) = delete;
pctrace(const pctrace &&) = delete;
pctrace(std::string const &);
virtual ~pctrace();
pctrace &operator=(const pctrace &) = delete;
pctrace &operator=(const pctrace &&) = delete;
bool registration(const char *const version, vm_if &arch) override;
sync_type get_sync() override { return POST_SYNC; };
void callback(instr_info_t) override;
private:
iss::instrumentation_if *instr_if {nullptr};
std::ofstream output;
#ifdef WITH_LZ4
std::unique_ptr<lz4compress_steambuf> strbuf;
std::ostream ostr;
#endif
std::string filename;
std::vector<instr_desc> delays;
bool jumped{false}, first{true};
};
}
}
#endif /* _ISS_PLUGIN_COV_H_ */

View File

@ -31,30 +31,24 @@
*******************************************************************************/
#include <iostream>
#include <iss/factory.h>
#include "iss/factory.h"
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <iss/arch/riscv_hart_m_p.h>
#include "iss/arch/riscv_hart_m_p.h"
#include "iss/arch/tgc_c.h"
using tgc_c_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_c>;
#ifdef CORE_TGC_B
#include "iss/arch/riscv_hart_m_p.h"
#include "iss/arch/tgc_b.h"
using tgc_b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_b>;
#endif
#ifdef CORE_TGC_D
#include "iss/arch/riscv_hart_mu_p.h"
#include "iss/arch/tgc_d.h"
using tgc_d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
#endif
#include "iss/arch/tgc_mapper.h"
#ifdef WITH_LLVM
#include <iss/llvm/jit_helper.h>
#endif
#include <iss/log_categories.h>
#include <iss/plugin/cycle_estimate.h>
#include <iss/plugin/instruction_count.h>
#include "iss/plugin/cycle_estimate.h"
#include "iss/plugin/instruction_count.h"
#include "iss/plugin/pctrace.h"
#ifndef WIN32
#include <iss/plugin/loader.h>
#endif
#if defined(HAS_LUA)
#include <iss/plugin/lua.h>
#endif
namespace po = boost::program_options;
@ -68,13 +62,13 @@ int main(int argc, char *argv[]) {
desc.add_options()
("help,h", "Print help message")
("verbose,v", po::value<int>()->implicit_value(0), "Sets logging verbosity")
("logfile,f", po::value<std::string>(), "Sets default log file.")
("logfile,l", po::value<std::string>(), "Sets default log file.")
("disass,d", po::value<std::string>()->implicit_value(""), "Enables disassembly")
("gdb-port,g", po::value<unsigned>()->default_value(0), "enable gdb server and specify port to use")
("instructions,i", po::value<uint64_t>()->default_value(std::numeric_limits<uint64_t>::max()), "max. number of instructions to simulate")
("reset,r", po::value<std::string>(), "reset address")
("dump-ir", "dump the intermediate representation")
("elf", po::value<std::vector<std::string>>(), "ELF file(s) to load")
("elf,f", po::value<std::vector<std::string>>(), "ELF file(s) to load")
("mem,m", po::value<std::string>(), "the memory input file")
("plugin,p", po::value<std::vector<std::string>>(), "plugin to activate")
("backend", po::value<std::string>()->default_value("interp"), "the memory input file")
@ -133,14 +127,46 @@ int main(int argc, char *argv[]) {
iss::create_cpu<tgc_b_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
} else
#endif
#ifdef CORE_TGC_C_XRB_NN
if (isa_opt == "tgc_c_xrb_nn") {
std::tie(cpu, vm) =
iss::create_cpu<tgc_c_xrb_nn_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
} else
#endif
#ifdef CORE_TGC_D
if (isa_opt == "tgc_d") {
std::tie(cpu, vm) =
iss::create_cpu<tgc_d_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
} else
#endif
#ifdef CORE_TGC_D_XRB_MAC
if (isa_opt == "tgc_d_xrb_mac") {
std::tie(cpu, vm) =
iss::create_cpu<tgc_d_xrb_mac_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
} else
#endif
#ifdef CORE_TGC_D_XRB_NN
if (isa_opt == "tgc_d_xrb_nn") {
std::tie(cpu, vm) =
iss::create_cpu<tgc_d_xrb_nn_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
} else
#endif
#ifdef CORE_TGC_E
if (isa_opt == "tgc_e") {
std::tie(cpu, vm) =
iss::create_cpu<tgc_e_plat_type>(clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>());
} else
#endif
{
LOG(ERR) << "Illegal argument value for '--isa': " << clim["isa"].as<std::string>() << std::endl;
LOG(ERR) << "Illegal argument value for '--isa': " << isa_opt << std::endl;
return 127;
}
if(!cpu ){
LOG(ERR) << "Could not create cpu for isa " << isa_opt << " and backend " <<clim["backend"].as<std::string>()<< std::endl;
return 127;
}
if(!vm ){
LOG(ERR) << "Could not create vm for isa " << isa_opt << " and backend " <<clim["backend"].as<std::string>()<< std::endl;
return 127;
}
if (clim.count("plugin")) {
@ -160,12 +186,27 @@ int main(int argc, char *argv[]) {
auto *ce_plugin = new iss::plugin::cycle_estimate(filename);
vm->register_plugin(*ce_plugin);
plugin_list.push_back(ce_plugin);
} else if (plugin_name == "pctrace") {
auto *plugin = new iss::plugin::pctrace(filename);
vm->register_plugin(*plugin);
plugin_list.push_back(plugin);
} else {
#ifndef WIN32
std::array<char const*, 1> a{{filename.c_str()}};
iss::plugin::loader l(plugin_name, {{"initPlugin"}});
auto* plugin = l.call_function<iss::vm_plugin*>("initPlugin", a.size(), a.data());
if(plugin){
vm->register_plugin(*plugin);
plugin_list.push_back(plugin);
} else
#endif
{
LOG(ERR) << "Unknown plugin name: " << plugin_name << ", valid names are 'ce', 'ic'" << std::endl;
return 127;
}
}
}
}
if (clim.count("disass")) {
vm->setDisassEnabled(true);
LOGGER(disass)::reporting_level() = logging::INFO;

View File

@ -1,821 +0,0 @@
//===- GCOV.cpp - LLVM coverage tool --------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// GCOV implements the interface to read and write coverage files that use
// 'gcov' format.
//
//===----------------------------------------------------------------------===//
#include "GCOV.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <system_error>
using namespace llvm;
//===----------------------------------------------------------------------===//
// GCOVFile implementation.
/// readGCNO - Read GCNO buffer.
bool GCOVFile::readGCNO(GCOVBuffer &Buffer) {
if (!Buffer.readGCNOFormat())
return false;
if (!Buffer.readGCOVVersion(Version))
return false;
if (!Buffer.readInt(Checksum))
return false;
while (true) {
if (!Buffer.readFunctionTag())
break;
auto GFun = make_unique<GCOVFunction>(*this);
if (!GFun->readGCNO(Buffer, Version))
return false;
Functions.push_back(std::move(GFun));
}
GCNOInitialized = true;
return true;
}
/// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be
/// called after readGCNO().
bool GCOVFile::readGCDA(GCOVBuffer &Buffer) {
assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()");
if (!Buffer.readGCDAFormat())
return false;
GCOV::GCOVVersion GCDAVersion;
if (!Buffer.readGCOVVersion(GCDAVersion))
return false;
if (Version != GCDAVersion) {
errs() << "GCOV versions do not match.\n";
return false;
}
uint32_t GCDAChecksum;
if (!Buffer.readInt(GCDAChecksum))
return false;
if (Checksum != GCDAChecksum) {
errs() << "File checksums do not match: " << Checksum
<< " != " << GCDAChecksum << ".\n";
return false;
}
for (size_t i = 0, e = Functions.size(); i < e; ++i) {
if (!Buffer.readFunctionTag()) {
errs() << "Unexpected number of functions.\n";
return false;
}
if (!Functions[i]->readGCDA(Buffer, Version))
return false;
}
if (Buffer.readObjectTag()) {
uint32_t Length;
uint32_t Dummy;
if (!Buffer.readInt(Length))
return false;
if (!Buffer.readInt(Dummy))
return false; // checksum
if (!Buffer.readInt(Dummy))
return false; // num
if (!Buffer.readInt(RunCount))
return false;
Buffer.advanceCursor(Length - 3);
}
while (Buffer.readProgramTag()) {
uint32_t Length;
if (!Buffer.readInt(Length))
return false;
Buffer.advanceCursor(Length);
++ProgramCount;
}
return true;
}
void GCOVFile::print(raw_ostream &OS) const {
for (const auto &FPtr : Functions)
FPtr->print(OS);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// dump - Dump GCOVFile content to dbgs() for debugging purposes.
LLVM_DUMP_METHOD void GCOVFile::dump() const {
print(dbgs());
}
#endif
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
void GCOVFile::collectLineCounts(FileInfo &FI) {
for (const auto &FPtr : Functions)
FPtr->collectLineCounts(FI);
FI.setRunCount(RunCount);
FI.setProgramCount(ProgramCount);
}
//===----------------------------------------------------------------------===//
// GCOVFunction implementation.
/// readGCNO - Read a function from the GCNO buffer. Return false if an error
/// occurs.
bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
uint32_t Dummy;
if (!Buff.readInt(Dummy))
return false; // Function header length
if (!Buff.readInt(Ident))
return false;
if (!Buff.readInt(Checksum))
return false;
if (Version != GCOV::V402) {
uint32_t CfgChecksum;
if (!Buff.readInt(CfgChecksum))
return false;
if (Parent.getChecksum() != CfgChecksum) {
errs() << "File checksums do not match: " << Parent.getChecksum()
<< " != " << CfgChecksum << " in (" << Name << ").\n";
return false;
}
}
if (!Buff.readString(Name))
return false;
if (!Buff.readString(Filename))
return false;
if (!Buff.readInt(LineNumber))
return false;
// read blocks.
if (!Buff.readBlockTag()) {
errs() << "Block tag not found.\n";
return false;
}
uint32_t BlockCount;
if (!Buff.readInt(BlockCount))
return false;
for (uint32_t i = 0, e = BlockCount; i != e; ++i) {
if (!Buff.readInt(Dummy))
return false; // Block flags;
Blocks.push_back(make_unique<GCOVBlock>(*this, i));
}
// read edges.
while (Buff.readEdgeTag()) {
uint32_t EdgeCount;
if (!Buff.readInt(EdgeCount))
return false;
EdgeCount = (EdgeCount - 1) / 2;
uint32_t BlockNo;
if (!Buff.readInt(BlockNo))
return false;
if (BlockNo >= BlockCount) {
errs() << "Unexpected block number: " << BlockNo << " (in " << Name
<< ").\n";
return false;
}
for (uint32_t i = 0, e = EdgeCount; i != e; ++i) {
uint32_t Dst;
if (!Buff.readInt(Dst))
return false;
Edges.push_back(make_unique<GCOVEdge>(*Blocks[BlockNo], *Blocks[Dst]));
GCOVEdge *Edge = Edges.back().get();
Blocks[BlockNo]->addDstEdge(Edge);
Blocks[Dst]->addSrcEdge(Edge);
if (!Buff.readInt(Dummy))
return false; // Edge flag
}
}
// read line table.
while (Buff.readLineTag()) {
uint32_t LineTableLength;
// Read the length of this line table.
if (!Buff.readInt(LineTableLength))
return false;
uint32_t EndPos = Buff.getCursor() + LineTableLength * 4;
uint32_t BlockNo;
// Read the block number this table is associated with.
if (!Buff.readInt(BlockNo))
return false;
if (BlockNo >= BlockCount) {
errs() << "Unexpected block number: " << BlockNo << " (in " << Name
<< ").\n";
return false;
}
GCOVBlock &Block = *Blocks[BlockNo];
// Read the word that pads the beginning of the line table. This may be a
// flag of some sort, but seems to always be zero.
if (!Buff.readInt(Dummy))
return false;
// Line information starts here and continues up until the last word.
if (Buff.getCursor() != (EndPos - sizeof(uint32_t))) {
StringRef F;
// Read the source file name.
if (!Buff.readString(F))
return false;
if (Filename != F) {
errs() << "Multiple sources for a single basic block: " << Filename
<< " != " << F << " (in " << Name << ").\n";
return false;
}
// Read lines up to, but not including, the null terminator.
while (Buff.getCursor() < (EndPos - 2 * sizeof(uint32_t))) {
uint32_t Line;
if (!Buff.readInt(Line))
return false;
// Line 0 means this instruction was injected by the compiler. Skip it.
if (!Line)
continue;
Block.addLine(Line);
}
// Read the null terminator.
if (!Buff.readInt(Dummy))
return false;
}
// The last word is either a flag or padding, it isn't clear which. Skip
// over it.
if (!Buff.readInt(Dummy))
return false;
}
return true;
}
/// readGCDA - Read a function from the GCDA buffer. Return false if an error
/// occurs.
bool GCOVFunction::readGCDA(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
uint32_t HeaderLength;
if (!Buff.readInt(HeaderLength))
return false; // Function header length
uint64_t EndPos = Buff.getCursor() + HeaderLength * sizeof(uint32_t);
uint32_t GCDAIdent;
if (!Buff.readInt(GCDAIdent))
return false;
if (Ident != GCDAIdent) {
errs() << "Function identifiers do not match: " << Ident
<< " != " << GCDAIdent << " (in " << Name << ").\n";
return false;
}
uint32_t GCDAChecksum;
if (!Buff.readInt(GCDAChecksum))
return false;
if (Checksum != GCDAChecksum) {
errs() << "Function checksums do not match: " << Checksum
<< " != " << GCDAChecksum << " (in " << Name << ").\n";
return false;
}
uint32_t CfgChecksum;
if (Version != GCOV::V402) {
if (!Buff.readInt(CfgChecksum))
return false;
if (Parent.getChecksum() != CfgChecksum) {
errs() << "File checksums do not match: " << Parent.getChecksum()
<< " != " << CfgChecksum << " (in " << Name << ").\n";
return false;
}
}
if (Buff.getCursor() < EndPos) {
StringRef GCDAName;
if (!Buff.readString(GCDAName))
return false;
if (Name != GCDAName) {
errs() << "Function names do not match: " << Name << " != " << GCDAName
<< ".\n";
return false;
}
}
if (!Buff.readArcTag()) {
errs() << "Arc tag not found (in " << Name << ").\n";
return false;
}
uint32_t Count;
if (!Buff.readInt(Count))
return false;
Count /= 2;
// This for loop adds the counts for each block. A second nested loop is
// required to combine the edge counts that are contained in the GCDA file.
for (uint32_t BlockNo = 0; Count > 0; ++BlockNo) {
// The last block is always reserved for exit block
if (BlockNo >= Blocks.size()) {
errs() << "Unexpected number of edges (in " << Name << ").\n";
return false;
}
if (BlockNo == Blocks.size() - 1)
errs() << "(" << Name << ") has arcs from exit block.\n";
GCOVBlock &Block = *Blocks[BlockNo];
for (size_t EdgeNo = 0, End = Block.getNumDstEdges(); EdgeNo < End;
++EdgeNo) {
if (Count == 0) {
errs() << "Unexpected number of edges (in " << Name << ").\n";
return false;
}
uint64_t ArcCount;
if (!Buff.readInt64(ArcCount))
return false;
Block.addCount(EdgeNo, ArcCount);
--Count;
}
Block.sortDstEdges();
}
return true;
}
/// getEntryCount - Get the number of times the function was called by
/// retrieving the entry block's count.
uint64_t GCOVFunction::getEntryCount() const {
return Blocks.front()->getCount();
}
/// getExitCount - Get the number of times the function returned by retrieving
/// the exit block's count.
uint64_t GCOVFunction::getExitCount() const {
return Blocks.back()->getCount();
}
void GCOVFunction::print(raw_ostream &OS) const {
OS << "===== " << Name << " (" << Ident << ") @ " << Filename << ":"
<< LineNumber << "\n";
for (const auto &Block : Blocks)
Block->print(OS);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// dump - Dump GCOVFunction content to dbgs() for debugging purposes.
LLVM_DUMP_METHOD void GCOVFunction::dump() const {
print(dbgs());
}
#endif
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
void GCOVFunction::collectLineCounts(FileInfo &FI) {
// If the line number is zero, this is a function that doesn't actually appear
// in the source file, so there isn't anything we can do with it.
if (LineNumber == 0)
return;
for (const auto &Block : Blocks)
Block->collectLineCounts(FI);
FI.addFunctionLine(Filename, LineNumber, this);
}
//===----------------------------------------------------------------------===//
// GCOVBlock implementation.
/// ~GCOVBlock - Delete GCOVBlock and its content.
GCOVBlock::~GCOVBlock() {
SrcEdges.clear();
DstEdges.clear();
Lines.clear();
}
/// addCount - Add to block counter while storing the edge count. If the
/// destination has no outgoing edges, also update that block's count too.
void GCOVBlock::addCount(size_t DstEdgeNo, uint64_t N) {
assert(DstEdgeNo < DstEdges.size()); // up to caller to ensure EdgeNo is valid
DstEdges[DstEdgeNo]->Count = N;
Counter += N;
if (!DstEdges[DstEdgeNo]->Dst.getNumDstEdges())
DstEdges[DstEdgeNo]->Dst.Counter += N;
}
/// sortDstEdges - Sort destination edges by block number, nop if already
/// sorted. This is required for printing branch info in the correct order.
void GCOVBlock::sortDstEdges() {
if (!DstEdgesAreSorted) {
SortDstEdgesFunctor SortEdges;
std::stable_sort(DstEdges.begin(), DstEdges.end(), SortEdges);
}
}
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
void GCOVBlock::collectLineCounts(FileInfo &FI) {
for (uint32_t N : Lines)
FI.addBlockLine(Parent.getFilename(), N, this);
}
void GCOVBlock::print(raw_ostream &OS) const {
OS << "Block : " << Number << " Counter : " << Counter << "\n";
if (!SrcEdges.empty()) {
OS << "\tSource Edges : ";
for (const GCOVEdge *Edge : SrcEdges)
OS << Edge->Src.Number << " (" << Edge->Count << "), ";
OS << "\n";
}
if (!DstEdges.empty()) {
OS << "\tDestination Edges : ";
for (const GCOVEdge *Edge : DstEdges)
OS << Edge->Dst.Number << " (" << Edge->Count << "), ";
OS << "\n";
}
if (!Lines.empty()) {
OS << "\tLines : ";
for (uint32_t N : Lines)
OS << (N) << ",";
OS << "\n";
}
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// dump - Dump GCOVBlock content to dbgs() for debugging purposes.
LLVM_DUMP_METHOD void GCOVBlock::dump() const {
print(dbgs());
}
#endif
//===----------------------------------------------------------------------===//
// FileInfo implementation.
// Safe integer division, returns 0 if numerator is 0.
static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) {
if (!Numerator)
return 0;
return Numerator / Divisor;
}
// This custom division function mimics gcov's branch ouputs:
// - Round to closest whole number
// - Only output 0% or 100% if it's exactly that value
static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) {
if (!Numerator)
return 0;
if (Numerator == Divisor)
return 100;
uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
if (Res == 0)
return 1;
if (Res == 100)
return 99;
return Res;
}
namespace {
struct formatBranchInfo {
formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total)
: Options(Options), Count(Count), Total(Total) {}
void print(raw_ostream &OS) const {
if (!Total)
OS << "never executed";
else if (Options.BranchCount)
OS << "taken " << Count;
else
OS << "taken " << branchDiv(Count, Total) << "%";
}
const GCOV::Options &Options;
uint64_t Count;
uint64_t Total;
};
static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) {
FBI.print(OS);
return OS;
}
class LineConsumer {
std::unique_ptr<MemoryBuffer> Buffer;
StringRef Remaining;
public:
LineConsumer(StringRef Filename) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
MemoryBuffer::getFileOrSTDIN(Filename);
if (std::error_code EC = BufferOrErr.getError()) {
errs() << Filename << ": " << EC.message() << "\n";
Remaining = "";
} else {
Buffer = std::move(BufferOrErr.get());
Remaining = Buffer->getBuffer();
}
}
bool empty() { return Remaining.empty(); }
void printNext(raw_ostream &OS, uint32_t LineNum) {
StringRef Line;
if (empty())
Line = "/*EOF*/";
else
std::tie(Line, Remaining) = Remaining.split("\n");
OS << format("%5u:", LineNum) << Line << "\n";
}
};
} // end anonymous namespace
/// Convert a path to a gcov filename. If PreservePaths is true, this
/// translates "/" to "#", ".." to "^", and drops ".", to match gcov.
static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) {
if (!PreservePaths)
return sys::path::filename(Filename).str();
// This behaviour is defined by gcov in terms of text replacements, so it's
// not likely to do anything useful on filesystems with different textual
// conventions.
llvm::SmallString<256> Result("");
StringRef::iterator I, S, E;
for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) {
if (*I != '/')
continue;
if (I - S == 1 && *S == '.') {
// ".", the current directory, is skipped.
} else if (I - S == 2 && *S == '.' && *(S + 1) == '.') {
// "..", the parent directory, is replaced with "^".
Result.append("^#");
} else {
if (S < I)
// Leave other components intact,
Result.append(S, I);
// And separate with "#".
Result.push_back('#');
}
S = I + 1;
}
if (S < I)
Result.append(S, I);
return Result.str();
}
std::string FileInfo::getCoveragePath(StringRef Filename,
StringRef MainFilename) {
if (Options.NoOutput)
// This is probably a bug in gcov, but when -n is specified, paths aren't
// mangled at all, and the -l and -p options are ignored. Here, we do the
// same.
return Filename;
std::string CoveragePath;
if (Options.LongFileNames && !Filename.equals(MainFilename))
CoveragePath =
mangleCoveragePath(MainFilename, Options.PreservePaths) + "##";
CoveragePath += mangleCoveragePath(Filename, Options.PreservePaths) + ".gcov";
return CoveragePath;
}
std::unique_ptr<raw_ostream>
FileInfo::openCoveragePath(StringRef CoveragePath) {
if (Options.NoOutput)
return llvm::make_unique<raw_null_ostream>();
std::error_code EC;
auto OS = llvm::make_unique<raw_fd_ostream>(CoveragePath, EC,
sys::fs::F_Text);
if (EC) {
errs() << EC.message() << "\n";
return llvm::make_unique<raw_null_ostream>();
}
return std::move(OS);
}
/// print - Print source files with collected line count information.
void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
StringRef GCNOFile, StringRef GCDAFile) {
SmallVector<StringRef, 4> Filenames;
for (const auto &LI : LineInfo)
Filenames.push_back(LI.first());
std::sort(Filenames.begin(), Filenames.end());
for (StringRef Filename : Filenames) {
auto AllLines = LineConsumer(Filename);
std::string CoveragePath = getCoveragePath(Filename, MainFilename);
std::unique_ptr<raw_ostream> CovStream = openCoveragePath(CoveragePath);
raw_ostream &CovOS = *CovStream;
CovOS << " -: 0:Source:" << Filename << "\n";
CovOS << " -: 0:Graph:" << GCNOFile << "\n";
CovOS << " -: 0:Data:" << GCDAFile << "\n";
CovOS << " -: 0:Runs:" << RunCount << "\n";
CovOS << " -: 0:Programs:" << ProgramCount << "\n";
const LineData &Line = LineInfo[Filename];
GCOVCoverage FileCoverage(Filename);
for (uint32_t LineIndex = 0; LineIndex < Line.LastLine || !AllLines.empty();
++LineIndex) {
if (Options.BranchInfo) {
FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex);
if (FuncsIt != Line.Functions.end())
printFunctionSummary(CovOS, FuncsIt->second);
}
BlockLines::const_iterator BlocksIt = Line.Blocks.find(LineIndex);
if (BlocksIt == Line.Blocks.end()) {
// No basic blocks are on this line. Not an executable line of code.
CovOS << " -:";
AllLines.printNext(CovOS, LineIndex + 1);
} else {
const BlockVector &Blocks = BlocksIt->second;
// Add up the block counts to form line counts.
DenseMap<const GCOVFunction *, bool> LineExecs;
uint64_t LineCount = 0;
for (const GCOVBlock *Block : Blocks) {
if (Options.AllBlocks) {
// Only take the highest block count for that line.
uint64_t BlockCount = Block->getCount();
LineCount = LineCount > BlockCount ? LineCount : BlockCount;
} else {
// Sum up all of the block counts.
LineCount += Block->getCount();
}
if (Options.FuncCoverage) {
// This is a slightly convoluted way to most accurately gather line
// statistics for functions. Basically what is happening is that we
// don't want to count a single line with multiple blocks more than
// once. However, we also don't simply want to give the total line
// count to every function that starts on the line. Thus, what is
// happening here are two things:
// 1) Ensure that the number of logical lines is only incremented
// once per function.
// 2) If there are multiple blocks on the same line, ensure that the
// number of lines executed is incremented as long as at least
// one of the blocks are executed.
const GCOVFunction *Function = &Block->getParent();
if (FuncCoverages.find(Function) == FuncCoverages.end()) {
std::pair<const GCOVFunction *, GCOVCoverage> KeyValue(
Function, GCOVCoverage(Function->getName()));
FuncCoverages.insert(KeyValue);
}
GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second;
if (LineExecs.find(Function) == LineExecs.end()) {
if (Block->getCount()) {
++FuncCoverage.LinesExec;
LineExecs[Function] = true;
} else {
LineExecs[Function] = false;
}
++FuncCoverage.LogicalLines;
} else if (!LineExecs[Function] && Block->getCount()) {
++FuncCoverage.LinesExec;
LineExecs[Function] = true;
}
}
}
if (LineCount == 0)
CovOS << " #####:";
else {
CovOS << format("%9" PRIu64 ":", LineCount);
++FileCoverage.LinesExec;
}
++FileCoverage.LogicalLines;
AllLines.printNext(CovOS, LineIndex + 1);
uint32_t BlockNo = 0;
uint32_t EdgeNo = 0;
for (const GCOVBlock *Block : Blocks) {
// Only print block and branch information at the end of the block.
if (Block->getLastLine() != LineIndex + 1)
continue;
if (Options.AllBlocks)
printBlockInfo(CovOS, *Block, LineIndex, BlockNo);
if (Options.BranchInfo) {
size_t NumEdges = Block->getNumDstEdges();
if (NumEdges > 1)
printBranchInfo(CovOS, *Block, FileCoverage, EdgeNo);
else if (Options.UncondBranch && NumEdges == 1)
printUncondBranchInfo(CovOS, EdgeNo,
(*Block->dst_begin())->Count);
}
}
}
}
FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage));
}
// FIXME: There is no way to detect calls given current instrumentation.
if (Options.FuncCoverage)
printFuncCoverage(InfoOS);
printFileCoverage(InfoOS);
}
/// printFunctionSummary - Print function and block summary.
void FileInfo::printFunctionSummary(raw_ostream &OS,
const FunctionVector &Funcs) const {
for (const GCOVFunction *Func : Funcs) {
uint64_t EntryCount = Func->getEntryCount();
uint32_t BlocksExec = 0;
for (const GCOVBlock &Block : Func->blocks())
if (Block.getNumDstEdges() && Block.getCount())
++BlocksExec;
OS << "function " << Func->getName() << " called " << EntryCount
<< " returned " << safeDiv(Func->getExitCount() * 100, EntryCount)
<< "% blocks executed "
<< safeDiv(BlocksExec * 100, Func->getNumBlocks() - 1) << "%\n";
}
}
/// printBlockInfo - Output counts for each block.
void FileInfo::printBlockInfo(raw_ostream &OS, const GCOVBlock &Block,
uint32_t LineIndex, uint32_t &BlockNo) const {
if (Block.getCount() == 0)
OS << " $$$$$:";
else
OS << format("%9" PRIu64 ":", Block.getCount());
OS << format("%5u-block %2u\n", LineIndex + 1, BlockNo++);
}
/// printBranchInfo - Print conditional branch probabilities.
void FileInfo::printBranchInfo(raw_ostream &OS, const GCOVBlock &Block,
GCOVCoverage &Coverage, uint32_t &EdgeNo) {
SmallVector<uint64_t, 16> BranchCounts;
uint64_t TotalCounts = 0;
for (const GCOVEdge *Edge : Block.dsts()) {
BranchCounts.push_back(Edge->Count);
TotalCounts += Edge->Count;
if (Block.getCount())
++Coverage.BranchesExec;
if (Edge->Count)
++Coverage.BranchesTaken;
++Coverage.Branches;
if (Options.FuncCoverage) {
const GCOVFunction *Function = &Block.getParent();
GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second;
if (Block.getCount())
++FuncCoverage.BranchesExec;
if (Edge->Count)
++FuncCoverage.BranchesTaken;
++FuncCoverage.Branches;
}
}
for (uint64_t N : BranchCounts)
OS << format("branch %2u ", EdgeNo++)
<< formatBranchInfo(Options, N, TotalCounts) << "\n";
}
/// printUncondBranchInfo - Print unconditional branch probabilities.
void FileInfo::printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo,
uint64_t Count) const {
OS << format("unconditional %2u ", EdgeNo++)
<< formatBranchInfo(Options, Count, Count) << "\n";
}
// printCoverage - Print generic coverage info used by both printFuncCoverage
// and printFileCoverage.
void FileInfo::printCoverage(raw_ostream &OS,
const GCOVCoverage &Coverage) const {
OS << format("Lines executed:%.2f%% of %u\n",
double(Coverage.LinesExec) * 100 / Coverage.LogicalLines,
Coverage.LogicalLines);
if (Options.BranchInfo) {
if (Coverage.Branches) {
OS << format("Branches executed:%.2f%% of %u\n",
double(Coverage.BranchesExec) * 100 / Coverage.Branches,
Coverage.Branches);
OS << format("Taken at least once:%.2f%% of %u\n",
double(Coverage.BranchesTaken) * 100 / Coverage.Branches,
Coverage.Branches);
} else {
OS << "No branches\n";
}
OS << "No calls\n"; // to be consistent with gcov
}
}
// printFuncCoverage - Print per-function coverage info.
void FileInfo::printFuncCoverage(raw_ostream &OS) const {
for (const auto &FC : FuncCoverages) {
const GCOVCoverage &Coverage = FC.second;
OS << "Function '" << Coverage.Name << "'\n";
printCoverage(OS, Coverage);
OS << "\n";
}
}
// printFileCoverage - Print per-file coverage info.
void FileInfo::printFileCoverage(raw_ostream &OS) const {
for (const auto &FC : FileCoverages) {
const std::string &Filename = FC.first;
const GCOVCoverage &Coverage = FC.second;
OS << "File '" << Coverage.Name << "'\n";
printCoverage(OS, Coverage);
if (!Options.NoOutput)
OS << Coverage.Name << ":creating '" << Filename << "'\n";
OS << "\n";
}
}

View File

@ -1,460 +0,0 @@
//===- GCOV.h - LLVM coverage tool ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This header provides the interface to read and write coverage files that
// use 'gcov' format.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_GCOV_H
#define LLVM_PROFILEDATA_GCOV_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
namespace llvm {
class GCOVFunction;
class GCOVBlock;
class FileInfo;
namespace GCOV {
enum GCOVVersion { V402, V404, V704 };
/// \brief A struct for passing gcov options between functions.
struct Options {
Options(bool A, bool B, bool C, bool F, bool P, bool U, bool L, bool N)
: AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F),
PreservePaths(P), UncondBranch(U), LongFileNames(L), NoOutput(N) {}
bool AllBlocks;
bool BranchInfo;
bool BranchCount;
bool FuncCoverage;
bool PreservePaths;
bool UncondBranch;
bool LongFileNames;
bool NoOutput;
};
} // end namespace GCOV
/// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific
/// read operations.
class GCOVBuffer {
public:
GCOVBuffer(MemoryBuffer *B) : Buffer(B) {}
/// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
bool readGCNOFormat() {
StringRef File = Buffer->getBuffer().slice(0, 4);
if (File != "oncg") {
errs() << "Unexpected file type: " << File << ".\n";
return false;
}
Cursor = 4;
return true;
}
/// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
bool readGCDAFormat() {
StringRef File = Buffer->getBuffer().slice(0, 4);
if (File != "adcg") {
errs() << "Unexpected file type: " << File << ".\n";
return false;
}
Cursor = 4;
return true;
}
/// readGCOVVersion - Read GCOV version.
bool readGCOVVersion(GCOV::GCOVVersion &Version) {
StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (VersionStr == "*204") {
Cursor += 4;
Version = GCOV::V402;
return true;
}
if (VersionStr == "*404") {
Cursor += 4;
Version = GCOV::V404;
return true;
}
if (VersionStr == "*704") {
Cursor += 4;
Version = GCOV::V704;
return true;
}
errs() << "Unexpected version: " << VersionStr << ".\n";
return false;
}
/// readFunctionTag - If cursor points to a function tag then increment the
/// cursor and return true otherwise return false.
bool readFunctionTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
Tag[3] != '\1') {
return false;
}
Cursor += 4;
return true;
}
/// readBlockTag - If cursor points to a block tag then increment the
/// cursor and return true otherwise return false.
bool readBlockTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x41' ||
Tag[3] != '\x01') {
return false;
}
Cursor += 4;
return true;
}
/// readEdgeTag - If cursor points to an edge tag then increment the
/// cursor and return true otherwise return false.
bool readEdgeTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x43' ||
Tag[3] != '\x01') {
return false;
}
Cursor += 4;
return true;
}
/// readLineTag - If cursor points to a line tag then increment the
/// cursor and return true otherwise return false.
bool readLineTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x45' ||
Tag[3] != '\x01') {
return false;
}
Cursor += 4;
return true;
}
/// readArcTag - If cursor points to an gcda arc tag then increment the
/// cursor and return true otherwise return false.
bool readArcTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\xa1' ||
Tag[3] != '\1') {
return false;
}
Cursor += 4;
return true;
}
/// readObjectTag - If cursor points to an object summary tag then increment
/// the cursor and return true otherwise return false.
bool readObjectTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
Tag[3] != '\xa1') {
return false;
}
Cursor += 4;
return true;
}
/// readProgramTag - If cursor points to a program summary tag then increment
/// the cursor and return true otherwise return false.
bool readProgramTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
Tag[3] != '\xa3') {
return false;
}
Cursor += 4;
return true;
}
bool readInt(uint32_t &Val) {
if (Buffer->getBuffer().size() < Cursor + 4) {
errs() << "Unexpected end of memory buffer: " << Cursor + 4 << ".\n";
return false;
}
StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor + 4);
Cursor += 4;
Val = *(const uint32_t *)(Str.data());
return true;
}
bool readInt64(uint64_t &Val) {
uint32_t Lo, Hi;
if (!readInt(Lo) || !readInt(Hi))
return false;
Val = ((uint64_t)Hi << 32) | Lo;
return true;
}
bool readString(StringRef &Str) {
uint32_t Len = 0;
// Keep reading until we find a non-zero length. This emulates gcov's
// behaviour, which appears to do the same.
while (Len == 0)
if (!readInt(Len))
return false;
Len *= 4;
if (Buffer->getBuffer().size() < Cursor + Len) {
errs() << "Unexpected end of memory buffer: " << Cursor + Len << ".\n";
return false;
}
Str = Buffer->getBuffer().slice(Cursor, Cursor + Len).split('\0').first;
Cursor += Len;
return true;
}
uint64_t getCursor() const { return Cursor; }
void advanceCursor(uint32_t n) { Cursor += n * 4; }
private:
MemoryBuffer *Buffer;
uint64_t Cursor = 0;
};
/// GCOVFile - Collects coverage information for one pair of coverage file
/// (.gcno and .gcda).
class GCOVFile {
public:
GCOVFile() = default;
bool readGCNO(GCOVBuffer &Buffer);
bool readGCDA(GCOVBuffer &Buffer);
uint32_t getChecksum() const { return Checksum; }
void print(raw_ostream &OS) const;
void dump() const;
void collectLineCounts(FileInfo &FI);
private:
bool GCNOInitialized = false;
GCOV::GCOVVersion Version;
uint32_t Checksum = 0;
SmallVector<std::unique_ptr<GCOVFunction>, 16> Functions;
uint32_t RunCount = 0;
uint32_t ProgramCount = 0;
};
/// GCOVEdge - Collects edge information.
struct GCOVEdge {
GCOVEdge(GCOVBlock &S, GCOVBlock &D) : Src(S), Dst(D) {}
GCOVBlock &Src;
GCOVBlock &Dst;
uint64_t Count = 0;
};
/// GCOVFunction - Collects function information.
class GCOVFunction {
public:
using BlockIterator = pointee_iterator<SmallVectorImpl<
std::unique_ptr<GCOVBlock>>::const_iterator>;
GCOVFunction(GCOVFile &P) : Parent(P) {}
bool readGCNO(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
bool readGCDA(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
StringRef getName() const { return Name; }
StringRef getFilename() const { return Filename; }
size_t getNumBlocks() const { return Blocks.size(); }
uint64_t getEntryCount() const;
uint64_t getExitCount() const;
BlockIterator block_begin() const { return Blocks.begin(); }
BlockIterator block_end() const { return Blocks.end(); }
iterator_range<BlockIterator> blocks() const {
return make_range(block_begin(), block_end());
}
void print(raw_ostream &OS) const;
void dump() const;
void collectLineCounts(FileInfo &FI);
private:
GCOVFile &Parent;
uint32_t Ident = 0;
uint32_t Checksum;
uint32_t LineNumber = 0;
StringRef Name;
StringRef Filename;
SmallVector<std::unique_ptr<GCOVBlock>, 16> Blocks;
SmallVector<std::unique_ptr<GCOVEdge>, 16> Edges;
};
/// GCOVBlock - Collects block information.
class GCOVBlock {
struct EdgeWeight {
EdgeWeight(GCOVBlock *D) : Dst(D) {}
GCOVBlock *Dst;
uint64_t Count = 0;
};
struct SortDstEdgesFunctor {
bool operator()(const GCOVEdge *E1, const GCOVEdge *E2) {
return E1->Dst.Number < E2->Dst.Number;
}
};
public:
using EdgeIterator = SmallVectorImpl<GCOVEdge *>::const_iterator;
GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N) {}
~GCOVBlock();
const GCOVFunction &getParent() const { return Parent; }
void addLine(uint32_t N) { Lines.push_back(N); }
uint32_t getLastLine() const { return Lines.back(); }
void addCount(size_t DstEdgeNo, uint64_t N);
uint64_t getCount() const { return Counter; }
void addSrcEdge(GCOVEdge *Edge) {
assert(&Edge->Dst == this); // up to caller to ensure edge is valid
SrcEdges.push_back(Edge);
}
void addDstEdge(GCOVEdge *Edge) {
assert(&Edge->Src == this); // up to caller to ensure edge is valid
// Check if adding this edge causes list to become unsorted.
if (DstEdges.size() && DstEdges.back()->Dst.Number > Edge->Dst.Number)
DstEdgesAreSorted = false;
DstEdges.push_back(Edge);
}
size_t getNumSrcEdges() const { return SrcEdges.size(); }
size_t getNumDstEdges() const { return DstEdges.size(); }
void sortDstEdges();
EdgeIterator src_begin() const { return SrcEdges.begin(); }
EdgeIterator src_end() const { return SrcEdges.end(); }
iterator_range<EdgeIterator> srcs() const {
return make_range(src_begin(), src_end());
}
EdgeIterator dst_begin() const { return DstEdges.begin(); }
EdgeIterator dst_end() const { return DstEdges.end(); }
iterator_range<EdgeIterator> dsts() const {
return make_range(dst_begin(), dst_end());
}
void print(raw_ostream &OS) const;
void dump() const;
void collectLineCounts(FileInfo &FI);
private:
GCOVFunction &Parent;
uint32_t Number;
uint64_t Counter = 0;
bool DstEdgesAreSorted = true;
SmallVector<GCOVEdge *, 16> SrcEdges;
SmallVector<GCOVEdge *, 16> DstEdges;
SmallVector<uint32_t, 16> Lines;
};
class FileInfo {
// It is unlikely--but possible--for multiple functions to be on the same
// line.
// Therefore this typedef allows LineData.Functions to store multiple
// functions
// per instance. This is rare, however, so optimize for the common case.
using FunctionVector = SmallVector<const GCOVFunction *, 1>;
using FunctionLines = DenseMap<uint32_t, FunctionVector>;
using BlockVector = SmallVector<const GCOVBlock *, 4>;
using BlockLines = DenseMap<uint32_t, BlockVector>;
struct LineData {
LineData() = default;
BlockLines Blocks;
FunctionLines Functions;
uint32_t LastLine = 0;
};
struct GCOVCoverage {
GCOVCoverage(StringRef Name) : Name(Name) {}
StringRef Name;
uint32_t LogicalLines = 0;
uint32_t LinesExec = 0;
uint32_t Branches = 0;
uint32_t BranchesExec = 0;
uint32_t BranchesTaken = 0;
};
public:
FileInfo(const GCOV::Options &Options) : Options(Options) {}
void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) {
if (Line > LineInfo[Filename].LastLine)
LineInfo[Filename].LastLine = Line;
LineInfo[Filename].Blocks[Line - 1].push_back(Block);
}
void addFunctionLine(StringRef Filename, uint32_t Line,
const GCOVFunction *Function) {
if (Line > LineInfo[Filename].LastLine)
LineInfo[Filename].LastLine = Line;
LineInfo[Filename].Functions[Line - 1].push_back(Function);
}
void setRunCount(uint32_t Runs) { RunCount = Runs; }
void setProgramCount(uint32_t Programs) { ProgramCount = Programs; }
void print(raw_ostream &OS, StringRef MainFilename, StringRef GCNOFile,
StringRef GCDAFile);
private:
std::string getCoveragePath(StringRef Filename, StringRef MainFilename);
std::unique_ptr<raw_ostream> openCoveragePath(StringRef CoveragePath);
void printFunctionSummary(raw_ostream &OS, const FunctionVector &Funcs) const;
void printBlockInfo(raw_ostream &OS, const GCOVBlock &Block,
uint32_t LineIndex, uint32_t &BlockNo) const;
void printBranchInfo(raw_ostream &OS, const GCOVBlock &Block,
GCOVCoverage &Coverage, uint32_t &EdgeNo);
void printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo,
uint64_t Count) const;
void printCoverage(raw_ostream &OS, const GCOVCoverage &Coverage) const;
void printFuncCoverage(raw_ostream &OS) const;
void printFileCoverage(raw_ostream &OS) const;
const GCOV::Options &Options;
StringMap<LineData> LineInfo;
uint32_t RunCount = 0;
uint32_t ProgramCount = 0;
using FileCoverageList = SmallVector<std::pair<std::string, GCOVCoverage>, 4>;
using FuncCoverageMap = MapVector<const GCOVFunction *, GCOVCoverage>;
FileCoverageList FileCoverages;
FuncCoverageMap FuncCoverages;
};
} // end namespace llvm
#endif // LLVM_SUPPORT_GCOV_H

View File

@ -1,94 +0,0 @@
/*******************************************************************************
* Copyright (C) 2017, MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial API and implementation
******************************************************************************/
#include "iss/plugin/cycle_estimate.h"
#include <iss/arch_if.h>
#include <util/logging.h>
#include <fstream>
iss::plugin::cycle_estimate::cycle_estimate(std::string config_file_name)
: arch_instr(nullptr)
{
if (config_file_name.length() > 0) {
std::ifstream is(config_file_name);
if (is.is_open()) {
try {
is >> root;
} catch (Json::RuntimeError &e) {
LOG(ERR) << "Could not parse input file " << config_file_name << ", reason: " << e.what();
}
} else {
LOG(ERR) << "Could not open input file " << config_file_name;
}
}
}
iss::plugin::cycle_estimate::~cycle_estimate() {
}
bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if& vm) {
arch_instr = vm.get_arch()->get_instrumentation_if();
if(!arch_instr) return false;
const std::string core_name = arch_instr->core_type_name();
Json::Value &val = root[core_name];
if(!val.isNull() && val.isArray()){
delays.reserve(val.size());
for(auto it:val){
auto name = it["name"];
auto size = it["size"];
auto delay = it["delay"];
if(!name.isString() || !size.isUInt() || !(delay.isUInt() || delay.isArray())) throw std::runtime_error("JSON parse error");
if(delay.isUInt()){
delays.push_back(instr_desc{size.asUInt(), delay.asUInt(), 0});
} else {
delays.push_back(instr_desc{size.asUInt(), delay[0].asUInt(), delay[1].asUInt()});
}
}
} else {
LOG(ERR)<<"plugin cycle_estimate: could not find an entry for "<<core_name<<" in JSON file"<<std::endl;
}
return true;
}
void iss::plugin::cycle_estimate::callback(instr_info_t instr_info, exec_info const&) {
assert(arch_instr && "No instrumentation interface available but callback executed");
auto entry = delays[instr_info.instr_id];
bool taken = (arch_instr->get_next_pc()-arch_instr->get_pc()) != (entry.size/8);
if (taken && entry.taken > 1)
arch_instr->set_curr_instr_cycles(entry.taken);
else if (entry.not_taken > 1)
arch_instr->set_curr_instr_cycles(entry.not_taken);
}

View File

@ -31,37 +31,33 @@
*******************************************************************************/
// 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"
#ifdef CORE_TGC_B
#include "iss/arch/riscv_hart_m_p.h"
#include "iss/arch/tgc_b.h"
using tgc_b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_b>;
#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>
#ifndef WIN32
#include <iss/plugin/loader.h>
#endif
#include "iss/arch/riscv_hart_m_p.h"
#include "iss/arch/tgc_c.h"
using tgc_c_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_c>;
#ifdef CORE_TGC_D
#include "iss/arch/riscv_hart_mu_p.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>;
#endif
#include "scc/report.h"
#include "core_complex.h"
#include <iss/arch/tgc_mapper.h>
#include <scc/report.h>
#include <util/ities.h>
#include <iostream>
#include <sstream>
#include <array>
#include <iss/plugin/cycle_estimate.h>
#include <iss/plugin/instruction_count.h>
#include <iss/plugin/pctrace.h>
// clang-format on
#define STR(X) #X
#define CREATE_CORE(CN) \
if (type == STR(CN)) { std::tie(cpu, vm) = create_core<CN ## _plat_type>(backend, gdb_port, hart_id); } else
#ifdef WITH_SCV
#ifdef HAS_SCV
#include <scv.h>
#else
#include <scv-tr.h>
@ -111,7 +107,8 @@ public:
heart_state_t &get_state() { return this->state; }
void notify_phase(iss::arch_if::exec_phase p) override {
if (p == iss::arch_if::ISTART) owner->sync(this->reg.icount);
if (p == iss::arch_if::ISTART)
owner->sync(this->instr_if.get_total_cycles());
}
sync_type needed_sync() const override { return PRE_SYNC; }
@ -120,7 +117,8 @@ public:
if (!owner->disass_output(pc, instr)) {
std::stringstream s;
s << "[p:" << lvl[this->reg.PRIV] << ";s:0x" << std::hex << std::setfill('0')
<< std::setw(sizeof(reg_t) * 2) << (reg_t)this->state.mstatus << std::dec << ";c:" << this->reg.icount << "]";
<< std::setw(sizeof(reg_t) * 2) << (reg_t)this->state.mstatus << std::dec << ";c:"
<< this->icount + this->cycle_offset << "]";
SCCDEBUG(owner->name())<<"disass: "
<< "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40)
<< std::setfill(' ') << std::left << instr << s.str();
@ -131,7 +129,7 @@ public:
if (addr.access && access_type::DEBUG)
return owner->read_mem_dbg(addr.val, length, data) ? Ok : Err;
else {
return owner->read_mem(addr.val, length, data, addr.access && access_type::FETCH) ? Ok : Err;
return owner->read_mem(addr.val, length, data, is_fetch(addr.access)) ? Ok : Err;
}
}
@ -180,26 +178,26 @@ public:
void wait_until(uint64_t flags) override {
SCCDEBUG(owner->name()) << "Sleeping until interrupt";
do {
while(this->pending_trap == 0 && (this->csr[arch::mip] & this->csr[arch::mie]) == 0) {
sc_core::wait(wfi_evt);
} while (this->reg.pending_trap == 0);
}
PLAT::wait_until(flags);
}
void local_irq(short id, bool value) {
reg_t mask = 0;
switch (id) {
case 16: // SW
case 3: // SW
mask = 1 << 3;
break;
case 17: // timer
case 7: // timer
mask = 1 << 7;
break;
case 18: // external
case 11: // external
mask = 1 << 11;
break;
default:
/* do nothing*/
if(id>15) mask = 1 << id;
break;
}
if (value) {
@ -209,7 +207,7 @@ public:
this->csr[arch::mip] &= ~mask;
this->check_interrupt();
if(value)
SCCTRACE(owner->name()) << "Triggering interrupt " << id << " Pending trap: " << this->reg.pending_trap;
SCCTRACE(owner->name()) << "Triggering interrupt " << id << " Pending trap: " << this->pending_trap;
}
private:
@ -293,6 +291,12 @@ public:
#endif
#ifdef CORE_TGC_D
CREATE_CORE(tgc_d)
#endif
#ifdef CORE_TGC_D_XRB_MAC
CREATE_CORE(tgc_d_xrb_mac)
#endif
#ifdef CORE_TGC_D_XRB_NN
CREATE_CORE(tgc_d_xrb_nn)
#endif
{
LOG(ERR) << "Illegal argument value for core type: " << type << std::endl;
@ -327,6 +331,7 @@ SC_HAS_PROCESS(core_complex);// NOLINT
#ifndef CWR_SYSTEMC
core_complex::core_complex(sc_module_name const& name)
: sc_module(name)
, fetch_lut(tlm_dmi_ext())
, read_lut(tlm_dmi_ext())
, write_lut(tlm_dmi_ext())
{
@ -336,7 +341,13 @@ core_complex::core_complex(sc_module_name const& name)
void core_complex::init(){
trc=new core_trace();
initiator.register_invalidate_direct_mem_ptr([=](uint64_t start, uint64_t end) -> void {
ibus.register_invalidate_direct_mem_ptr([=](uint64_t start, uint64_t end) -> void {
auto lut_entry = fetch_lut.getEntry(start);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE && end <= lut_entry.get_end_address() + 1) {
fetch_lut.removeEntry(lut_entry);
}
});
dbus.register_invalidate_direct_mem_ptr([=](uint64_t start, uint64_t end) -> void {
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) {
read_lut.removeEntry(lut_entry);
@ -354,8 +365,11 @@ void core_complex::init(){
sensitive << sw_irq_i;
SC_METHOD(timer_irq_cb);
sensitive << timer_irq_i;
SC_METHOD(global_irq_cb);
sensitive << global_irq_i;
SC_METHOD(ext_irq_cb);
sensitive << ext_irq_i;
SC_METHOD(local_irq_cb);
for(auto pin:local_irq_i)
sensitive << pin;
trc->m_db=scv_tr_db::get_default_db();
SC_METHOD(forward);
@ -372,6 +386,8 @@ void core_complex::init(){
core_complex::~core_complex(){
delete cpu;
delete trc;
for (auto *p : plugin_list)
delete p;
}
void core_complex::trace(sc_trace_file *trf) const {}
@ -383,10 +399,47 @@ void core_complex::before_end_of_elaboration() {
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);
cpu->vm->setDisassEnabled(GET_PROP_VALUE(enable_disass) || trc->m_db != nullptr);
if (GET_PROP_VALUE(plugins).length()) {
auto p = util::split(GET_PROP_VALUE(plugins), ';');
for (std::string const& opt_val : p) {
std::string plugin_name=opt_val;
std::string filename{"cycles.txt"};
std::size_t found = opt_val.find('=');
if (found != std::string::npos) {
plugin_name = opt_val.substr(0, found);
filename = opt_val.substr(found + 1, opt_val.size());
}
if (plugin_name == "ic") {
auto *plugin = new iss::plugin::instruction_count(filename);
cpu->vm->register_plugin(*plugin);
plugin_list.push_back(plugin);
} else if (plugin_name == "ce") {
auto *plugin = new iss::plugin::cycle_estimate(filename);
cpu->vm->register_plugin(*plugin);
plugin_list.push_back(plugin);
} else if (plugin_name == "pctrace") {
auto *plugin = new iss::plugin::pctrace(filename);
cpu->vm->register_plugin(*plugin);
plugin_list.push_back(plugin);
} else {
#ifndef WIN32
std::array<char const*, 1> a{{filename.c_str()}};
iss::plugin::loader l(plugin_name, {{"initPlugin"}});
auto* plugin = l.call_function<iss::vm_plugin*>("initPlugin", a.size(), a.data());
if(plugin){
cpu->vm->register_plugin(*plugin);
plugin_list.push_back(plugin);
} else
#endif
SCCERR(SCMOD) << "Unknown plugin '" << plugin_name << "' or plugin not found";
}
}
}
}
void core_complex::start_of_simulation() {
quantum_keeper.reset();
// quantum_keeper.reset();
if (GET_PROP_VALUE(elf_file).size() > 0) {
istringstream is(GET_PROP_VALUE(elf_file));
string s;
@ -438,15 +491,24 @@ void core_complex::rst_cb() {
if (rst_i.read()) cpu->set_interrupt_execution(true);
}
void core_complex::sw_irq_cb() { cpu->local_irq(16, sw_irq_i.read()); }
void core_complex::sw_irq_cb() { cpu->local_irq(3, sw_irq_i.read()); }
void core_complex::timer_irq_cb() { cpu->local_irq(17, timer_irq_i.read()); }
void core_complex::timer_irq_cb() { cpu->local_irq(7, timer_irq_i.read()); }
void core_complex::global_irq_cb() { cpu->local_irq(18, global_irq_i.read()); }
void core_complex::ext_irq_cb() { cpu->local_irq(11, ext_irq_i.read()); }
void core_complex::local_irq_cb() {
for(auto i=0U; i<local_irq_i.size(); ++i) {
if(local_irq_i[i].event()) {
cpu->local_irq(16+i, local_irq_i[i].read());
}
}
}
void core_complex::run() {
wait(SC_ZERO_TIME); // separate from elaboration phase
do {
wait(SC_ZERO_TIME);
if (rst_i.read()) {
cpu->reset(GET_PROP_VALUE(reset_address));
wait(rst_i.negedge_event());
@ -454,6 +516,7 @@ void core_complex::run() {
while (curr_clk.read() == SC_ZERO_TIME) {
wait(curr_clk.value_changed_event());
}
quantum_keeper.reset();
cpu->set_interrupt_execution(false);
cpu->start();
} while (cpu->get_interrupt_execution());
@ -461,14 +524,15 @@ void core_complex::run() {
}
bool core_complex::read_mem(uint64_t addr, unsigned length, uint8_t *const data, bool is_fetch) {
auto lut_entry = read_lut.getEntry(addr);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE &&
addr + length <= lut_entry.get_end_address() + 1) {
auto& dmi_lut = is_fetch?fetch_lut:read_lut;
auto lut_entry = dmi_lut.getEntry(addr);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE && addr + length <= lut_entry.get_end_address() + 1) {
auto offset = addr - lut_entry.get_start_address();
std::copy(lut_entry.get_dmi_ptr() + offset, lut_entry.get_dmi_ptr() + offset + length, data);
quantum_keeper.inc(lut_entry.get_read_latency());
return true;
} else {
auto& sckt = is_fetch? ibus : dbus;
tlm::tlm_generic_payload gp;
gp.set_command(tlm::TLM_READ_COMMAND);
gp.set_address(addr);
@ -483,8 +547,13 @@ bool core_complex::read_mem(uint64_t addr, unsigned length, uint8_t *const data,
auto preExt = new tlm::scc::scv::tlm_recording_extension(trc->tr_handle, this);
gp.set_extension(preExt);
}
initiator->b_transport(gp, delay);
SCCTRACE(this->name()) << "read_mem(0x" << std::hex << addr << ") : " << data;
sckt->b_transport(gp, delay);
auto incr = delay-quantum_keeper.get_local_time();
if(is_fetch)
ibus_inc+=incr;
else
dbus_inc+=incr;
SCCTRACE(this->name()) << "[local time: "<<delay<<"]: finish read_mem(0x" << std::hex << addr << ") : 0x" << (length==4?*(uint32_t*)data:length==2?*(uint16_t*)data:(unsigned)*data);
if (gp.get_response_status() != tlm::TLM_OK_RESPONSE) {
return false;
}
@ -492,12 +561,9 @@ bool core_complex::read_mem(uint64_t addr, unsigned length, uint8_t *const data,
gp.set_command(tlm::TLM_READ_COMMAND);
gp.set_address(addr);
tlm_dmi_ext dmi_data;
if (initiator->get_direct_mem_ptr(gp, dmi_data)) {
if (sckt->get_direct_mem_ptr(gp, dmi_data)) {
if (dmi_data.is_read_allowed())
read_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_data.get_end_address() - dmi_data.get_start_address() + 1);
if (dmi_data.is_write_allowed())
write_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_data.get_end_address() - dmi_data.get_start_address() + 1);
}
}
@ -527,9 +593,9 @@ bool core_complex::write_mem(uint64_t addr, unsigned length, const uint8_t *cons
auto preExt = new tlm::scc::scv::tlm_recording_extension(trc->tr_handle, this);
gp.set_extension(preExt);
}
initiator->b_transport(gp, delay);
quantum_keeper.set(delay);
SCCTRACE() << "write_mem(0x" << std::hex << addr << ") : " << data;
dbus->b_transport(gp, delay);
dbus_inc+=delay-quantum_keeper.get_local_time();
SCCTRACE() << "[local time: "<<delay<<"]: finish write_mem(0x" << std::hex << addr << ") : 0x" << (length==4?*(uint32_t*)data:length==2?*(uint16_t*)data:(unsigned)*data);
if (gp.get_response_status() != tlm::TLM_OK_RESPONSE) {
return false;
}
@ -537,10 +603,7 @@ bool core_complex::write_mem(uint64_t addr, unsigned length, const uint8_t *cons
gp.set_command(tlm::TLM_READ_COMMAND);
gp.set_address(addr);
tlm_dmi_ext dmi_data;
if (initiator->get_direct_mem_ptr(gp, dmi_data)) {
if (dmi_data.is_read_allowed())
read_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_data.get_end_address() - dmi_data.get_start_address() + 1);
if (dbus->get_direct_mem_ptr(gp, dmi_data)) {
if (dmi_data.is_write_allowed())
write_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_data.get_end_address() - dmi_data.get_start_address() + 1);
@ -551,33 +614,16 @@ bool core_complex::write_mem(uint64_t addr, unsigned length, const uint8_t *cons
}
bool core_complex::read_mem_dbg(uint64_t addr, unsigned length, uint8_t *const data) {
auto lut_entry = read_lut.getEntry(addr);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE &&
addr + length <= lut_entry.get_end_address() + 1) {
auto offset = addr - lut_entry.get_start_address();
std::copy(lut_entry.get_dmi_ptr() + offset, lut_entry.get_dmi_ptr() + offset + length, data);
quantum_keeper.inc(lut_entry.get_read_latency());
return true;
} else {
tlm::tlm_generic_payload gp;
gp.set_command(tlm::TLM_READ_COMMAND);
gp.set_address(addr);
gp.set_data_ptr(data);
gp.set_data_length(length);
gp.set_streaming_width(length);
return initiator->transport_dbg(gp) == length;
}
return dbus->transport_dbg(gp) == length;
}
bool core_complex::write_mem_dbg(uint64_t addr, unsigned length, const uint8_t *const data) {
auto lut_entry = write_lut.getEntry(addr);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE &&
addr + length <= lut_entry.get_end_address() + 1) {
auto offset = addr - lut_entry.get_start_address();
std::copy(data, data + length, lut_entry.get_dmi_ptr() + offset);
quantum_keeper.inc(lut_entry.get_read_latency());
return true;
} else {
write_buf.resize(length);
std::copy(data, data + length, write_buf.begin()); // need to copy as TLM does not guarantee data integrity
tlm::tlm_generic_payload gp;
@ -586,8 +632,7 @@ bool core_complex::write_mem_dbg(uint64_t addr, unsigned length, const uint8_t *
gp.set_data_ptr(write_buf.data());
gp.set_data_length(length);
gp.set_streaming_width(length);
return initiator->transport_dbg(gp) == length;
}
return dbus->transport_dbg(gp) == length;
}
} /* namespace SiFive */
} /* namespace sysc */

View File

@ -40,14 +40,19 @@
#include <tlm/scc/scv/tlm_rec_initiator_socket.h>
#ifdef CWR_SYSTEMC
#include <scmlinc/scml_property.h>
#define SOCKET_WIDTH 32
#else
#include <cci_configuration>
#define SOCKET_WIDTH scc::LT
#endif
#include <tlm>
#include <tlm_utils/tlm_quantumkeeper.h>
#include <util/range_lut.h>
#include <memory>
namespace iss {
class vm_plugin;
}
namespace sysc {
class tlm_dmi_ext : public tlm::tlm_dmi {
@ -66,11 +71,13 @@ struct core_trace;
class core_complex : public sc_core::sc_module, public scc::traceable {
public:
tlm::scc::initiator_mixin<tlm::scc::scv::tlm_rec_initiator_socket<32>> initiator{"intor"};
tlm::scc::initiator_mixin<tlm::tlm_initiator_socket<SOCKET_WIDTH>> ibus{"ibus"};
tlm::scc::initiator_mixin<tlm::tlm_initiator_socket<SOCKET_WIDTH>> dbus{"dbus"};
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> ext_irq_i{"ext_irq_i"};
sc_core::sc_in<bool> timer_irq_i{"timer_irq_i"};
@ -81,7 +88,7 @@ public:
#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{"mtime_o"};
cci::cci_param<std::string> elf_file{"elf_file", ""};
@ -99,6 +106,8 @@ public:
cci::cci_param<uint32_t> mhartid{"mhartid", 0};
cci::cci_param<std::string> plugins{"plugins", ""};
core_complex(sc_core::sc_module_name const& name);
#else
@ -122,6 +131,8 @@ public:
scml_property<uint32_t> mhartid{"mhartid", 0};
scml_property<std::string> plugins{"plugins", ""};
core_complex(sc_core::sc_module_name const& name)
: sc_module(name)
, local_irq_i{"local_irq_i", 16}
@ -133,6 +144,8 @@ public:
, gdb_server_port{"gdb_server_port", 0}
, dump_ir{"dump_ir", false}
, mhartid{"mhartid", 0}
, plugins{"plugins", ""}
, fetch_lut(tlm_dmi_ext())
, read_lut(tlm_dmi_ext())
, write_lut(tlm_dmi_ext())
{
@ -144,13 +157,16 @@ public:
~core_complex();
inline void sync(uint64_t cycle) {
auto time = curr_clk * (cycle - last_sync_cycle);
quantum_keeper.inc(time);
auto core_inc = curr_clk * (cycle - last_sync_cycle);
auto incr = std::max(core_inc, std::max(ibus_inc, dbus_inc));
quantum_keeper.inc(incr);
if (quantum_keeper.need_sync()) {
wait(quantum_keeper.get_local_time());
quantum_keeper.reset();
}
last_sync_cycle = cycle;
ibus_inc = sc_core::SC_ZERO_TIME;
dbus_inc = sc_core::SC_ZERO_TIME;
}
bool read_mem(uint64_t addr, unsigned length, uint8_t *const data, bool is_fetch);
@ -174,19 +190,23 @@ protected:
void rst_cb();
void sw_irq_cb();
void timer_irq_cb();
void global_irq_cb();
void ext_irq_cb();
void local_irq_cb();
uint64_t last_sync_cycle = 0;
util::range_lut<tlm_dmi_ext> read_lut, write_lut;
util::range_lut<tlm_dmi_ext> fetch_lut, read_lut, write_lut;
tlm_utils::tlm_quantumkeeper quantum_keeper;
std::vector<uint8_t> write_buf;
core_wrapper* cpu{nullptr};
sc_core::sc_signal<sc_core::sc_time> curr_clk;
sc_core::sc_time ibus_inc, dbus_inc;
core_trace* trc{nullptr};
std::unique_ptr<scc::tick2time> t2t;
private:
void init();
std::vector<iss::vm_plugin *> plugin_list;
};
} /* namespace SiFive */
} /* namespace tgfs */
} /* namespace sysc */
#endif /* _SYSC_CORE_COMPLEX_H_ */

View File

@ -1 +0,0 @@
/vm_tgc_*.cpp

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@
*
*******************************************************************************/
#include <iss/arch/tgf_c.h>
#include <iss/arch/tgc_c.h>
#include <iss/arch/riscv_hart_m_p.h>
#include <iss/debugger/gdb_session.h>
#include <iss/debugger/server.h>
@ -52,7 +52,7 @@ namespace fp_impl {
void add_fp_functions_2_module(::llvm::Module *, unsigned, unsigned);
}
namespace tgf_c {
namespace tgc_c {
using namespace ::llvm;
using namespace iss::arch;
using namespace iss::debugger;
@ -4151,11 +4151,11 @@ template <typename ARCH> inline void vm_impl<ARCH>::gen_trap_check(BasicBlock *b
bb, this->trap_blk, 1);
}
} // namespace tgf_c
} // namespace tgc_c
template <>
std::unique_ptr<vm_if> create<arch::tgf_c>(arch::tgf_c *core, unsigned short port, bool dump) {
auto ret = new tgf_c::vm_impl<arch::tgf_c>(*core, dump);
std::unique_ptr<vm_if> create<arch::tgc_c>(arch::tgc_c *core, unsigned short port, bool dump) {
auto ret = new tgc_c::vm_impl<arch::tgc_c>(*core, dump);
if (port != 0) debugger::server<debugger::gdb_session>::run_server(ret, port);
return std::unique_ptr<vm_if>(ret);
}

File diff suppressed because it is too large Load Diff

3218
src/vm/tcc/vm_tgc_c.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff