mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
riscv64: spec compliance and regression test fix (#698)
* spec compliance Signed-off-by: Akif Ejaz <akifejaz40@gmail.com> * revert the demo changes Signed-off-by: Akif Ejaz <akifejaz40@gmail.com> * Restored the FPU demo hook so the RISC-V64 functional test can pass The new check-functional-riscv64 target verifies FPU context switching by watching fpu_test_val advance by 1.1f on each pass through thread_6_and_7_entry. The GDB script deliberately treats a missing symbol as a failure rather than silently skipping the check, but the demo no longer defined it, so the target failed on every run: FPU_VERIFIED_FAIL_NO_SYMBOL The definition and the increment are restored, matching what the risc-v32 demo already carries. The functional target now passes end to end. Three small corrections are folded in: - tx_port.h carried a comment stating that the ISA string must include Zicsr, but nothing enforced it, so an rv64imac build failed with a wall of assembler "unrecognized opcode" errors. It now stops at one clear diagnostic. - Removed TX_RISCV_TRAP_CALL_FRAME_SIZE, which nothing referenced. - The example .gitignore listed qemu-riscv32.log, but the runner writes qemu-riscv64.log, so the generated log showed up as an untracked file. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> --------- Signed-off-by: Akif Ejaz <akifejaz40@gmail.com> Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
This commit is contained in:
co-authored by
Frédéric Desbiens
parent
515ab8aba1
commit
164f211a01
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,76 @@
|
||||
# This file will be configured to contain variables for CPack. These variables
|
||||
# should be set in the CMake list file of the project before CPack module is
|
||||
# included. The list of available CPACK_xxx variables and their associated
|
||||
# documentation may be obtained using
|
||||
# cpack --help-variable-list
|
||||
#
|
||||
# Some variables are common to all generators (e.g. CPACK_PACKAGE_NAME)
|
||||
# and some are specific to a generator
|
||||
# (e.g. CPACK_NSIS_EXTRA_INSTALL_COMMANDS). The generator specific variables
|
||||
# usually begin with CPACK_<GENNAME>_xxxx.
|
||||
|
||||
|
||||
set(CPACK_ARCHIVE_GID "-1")
|
||||
set(CPACK_ARCHIVE_UID "-1")
|
||||
set(CPACK_BINARY_7Z "OFF")
|
||||
set(CPACK_BINARY_IFW "OFF")
|
||||
set(CPACK_BINARY_INNOSETUP "OFF")
|
||||
set(CPACK_BINARY_NSIS "ON")
|
||||
set(CPACK_BINARY_NUGET "OFF")
|
||||
set(CPACK_BINARY_WIX "OFF")
|
||||
set(CPACK_BINARY_ZIP "OFF")
|
||||
set(CPACK_BUILD_SOURCE_DIRS "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698;/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64")
|
||||
set(CPACK_CMAKE_GENERATOR "Ninja")
|
||||
set(CPACK_COMPONENTS_ALL "")
|
||||
set(CPACK_COMPONENT_UNSPECIFIED_HIDDEN "TRUE")
|
||||
set(CPACK_COMPONENT_UNSPECIFIED_REQUIRED "TRUE")
|
||||
set(CPACK_DEFAULT_PACKAGE_DESCRIPTION_FILE "/usr/share/cmake-4.4/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_DEFAULT_PACKAGE_DESCRIPTION_SUMMARY "threadx built using CMake")
|
||||
set(CPACK_GENERATOR "NSIS")
|
||||
set(CPACK_INNOSETUP_ARCHITECTURE "x64")
|
||||
set(CPACK_INSTALL_CMAKE_PROJECTS "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64;threadx;ALL;/")
|
||||
set(CPACK_INSTALL_PREFIX "/usr/local")
|
||||
set(CPACK_MODULE_PATH "")
|
||||
set(CPACK_NSIS_DISPLAY_NAME "threadx 0.1.1")
|
||||
set(CPACK_NSIS_INSTALLER_ICON_CODE "")
|
||||
set(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "")
|
||||
set(CPACK_NSIS_INSTALL_ROOT "\$PROGRAMFILES")
|
||||
set(CPACK_NSIS_PACKAGE_NAME "threadx 0.1.1")
|
||||
set(CPACK_NSIS_UNINSTALL_NAME "Uninstall")
|
||||
set(CPACK_OBJCOPY_EXECUTABLE "/opt/riscv/bin/riscv64-unknown-elf-objcopy")
|
||||
set(CPACK_OBJDUMP_EXECUTABLE "/opt/riscv/bin/riscv64-unknown-elf-objdump")
|
||||
set(CPACK_OUTPUT_CONFIG_FILE "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64/CPackConfig.cmake")
|
||||
set(CPACK_PACKAGE_DEFAULT_LOCATION "/")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "/usr/share/cmake-4.4/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "threadx built using CMake")
|
||||
set(CPACK_PACKAGE_FILE_NAME "threadx-0.1.1-Generic")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "threadx 0.1.1")
|
||||
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "threadx 0.1.1")
|
||||
set(CPACK_PACKAGE_NAME "threadx")
|
||||
set(CPACK_PACKAGE_RELOCATABLE "true")
|
||||
set(CPACK_PACKAGE_VENDOR "Humanity")
|
||||
set(CPACK_PACKAGE_VERSION "0.1.1")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "1")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "1")
|
||||
set(CPACK_READELF_EXECUTABLE "/opt/riscv/bin/riscv64-unknown-elf-readelf")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "/usr/share/cmake-4.4/Templates/CPack.GenericLicense.txt")
|
||||
set(CPACK_RESOURCE_FILE_README "/usr/share/cmake-4.4/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_RESOURCE_FILE_WELCOME "/usr/share/cmake-4.4/Templates/CPack.GenericWelcome.txt")
|
||||
set(CPACK_SET_DESTDIR "OFF")
|
||||
set(CPACK_SOURCE_GENERATOR "ZIP")
|
||||
set(CPACK_SOURCE_IGNORE_FILES "\\.git/;\\.github/;_build/;\\.git;\\.gitattributes;\\.gitignore;.*~\$")
|
||||
set(CPACK_SOURCE_OUTPUT_CONFIG_FILE "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64/CPackSourceConfig.cmake")
|
||||
set(CPACK_SYSTEM_NAME "Generic")
|
||||
set(CPACK_THREADS "1")
|
||||
set(CPACK_TOPLEVEL_TAG "Generic")
|
||||
set(CPACK_VERBATIM_VARIABLES "YES")
|
||||
set(CPACK_WIX_SIZEOF_VOID_P "8")
|
||||
|
||||
if(NOT CPACK_PROPERTIES_FILE)
|
||||
set(CPACK_PROPERTIES_FILE "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64/CPackProperties.cmake")
|
||||
endif()
|
||||
|
||||
if(EXISTS ${CPACK_PROPERTIES_FILE})
|
||||
include(${CPACK_PROPERTIES_FILE})
|
||||
endif()
|
||||
@@ -0,0 +1,83 @@
|
||||
# This file will be configured to contain variables for CPack. These variables
|
||||
# should be set in the CMake list file of the project before CPack module is
|
||||
# included. The list of available CPACK_xxx variables and their associated
|
||||
# documentation may be obtained using
|
||||
# cpack --help-variable-list
|
||||
#
|
||||
# Some variables are common to all generators (e.g. CPACK_PACKAGE_NAME)
|
||||
# and some are specific to a generator
|
||||
# (e.g. CPACK_NSIS_EXTRA_INSTALL_COMMANDS). The generator specific variables
|
||||
# usually begin with CPACK_<GENNAME>_xxxx.
|
||||
|
||||
|
||||
set(CPACK_ARCHIVE_GID "-1")
|
||||
set(CPACK_ARCHIVE_UID "-1")
|
||||
set(CPACK_BINARY_7Z "OFF")
|
||||
set(CPACK_BINARY_IFW "OFF")
|
||||
set(CPACK_BINARY_INNOSETUP "OFF")
|
||||
set(CPACK_BINARY_NSIS "ON")
|
||||
set(CPACK_BINARY_NUGET "OFF")
|
||||
set(CPACK_BINARY_WIX "OFF")
|
||||
set(CPACK_BINARY_ZIP "OFF")
|
||||
set(CPACK_BUILD_SOURCE_DIRS "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698;/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64")
|
||||
set(CPACK_CMAKE_GENERATOR "Ninja")
|
||||
set(CPACK_COMPONENTS_ALL "")
|
||||
set(CPACK_COMPONENT_UNSPECIFIED_HIDDEN "TRUE")
|
||||
set(CPACK_COMPONENT_UNSPECIFIED_REQUIRED "TRUE")
|
||||
set(CPACK_DEFAULT_PACKAGE_DESCRIPTION_FILE "/usr/share/cmake-4.4/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_DEFAULT_PACKAGE_DESCRIPTION_SUMMARY "threadx built using CMake")
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
set(CPACK_IGNORE_FILES "\\.git/;\\.github/;_build/;\\.git;\\.gitattributes;\\.gitignore;.*~\$")
|
||||
set(CPACK_INNOSETUP_ARCHITECTURE "x64")
|
||||
set(CPACK_INSTALLED_DIRECTORIES "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698;/")
|
||||
set(CPACK_INSTALL_CMAKE_PROJECTS "")
|
||||
set(CPACK_INSTALL_PREFIX "/usr/local")
|
||||
set(CPACK_MODULE_PATH "")
|
||||
set(CPACK_NSIS_DISPLAY_NAME "threadx 0.1.1")
|
||||
set(CPACK_NSIS_INSTALLER_ICON_CODE "")
|
||||
set(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "")
|
||||
set(CPACK_NSIS_INSTALL_ROOT "\$PROGRAMFILES")
|
||||
set(CPACK_NSIS_PACKAGE_NAME "threadx 0.1.1")
|
||||
set(CPACK_NSIS_UNINSTALL_NAME "Uninstall")
|
||||
set(CPACK_OBJCOPY_EXECUTABLE "/opt/riscv/bin/riscv64-unknown-elf-objcopy")
|
||||
set(CPACK_OBJDUMP_EXECUTABLE "/opt/riscv/bin/riscv64-unknown-elf-objdump")
|
||||
set(CPACK_OUTPUT_CONFIG_FILE "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64/CPackConfig.cmake")
|
||||
set(CPACK_PACKAGE_DEFAULT_LOCATION "/")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "/usr/share/cmake-4.4/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "threadx built using CMake")
|
||||
set(CPACK_PACKAGE_FILE_NAME "threadx-0.1.1-Source")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "threadx 0.1.1")
|
||||
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "threadx 0.1.1")
|
||||
set(CPACK_PACKAGE_NAME "threadx")
|
||||
set(CPACK_PACKAGE_RELOCATABLE "true")
|
||||
set(CPACK_PACKAGE_VENDOR "Humanity")
|
||||
set(CPACK_PACKAGE_VERSION "0.1.1")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "1")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "1")
|
||||
set(CPACK_READELF_EXECUTABLE "/opt/riscv/bin/riscv64-unknown-elf-readelf")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "/usr/share/cmake-4.4/Templates/CPack.GenericLicense.txt")
|
||||
set(CPACK_RESOURCE_FILE_README "/usr/share/cmake-4.4/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_RESOURCE_FILE_WELCOME "/usr/share/cmake-4.4/Templates/CPack.GenericWelcome.txt")
|
||||
set(CPACK_RPM_PACKAGE_SOURCES "ON")
|
||||
set(CPACK_SET_DESTDIR "OFF")
|
||||
set(CPACK_SOURCE_GENERATOR "ZIP")
|
||||
set(CPACK_SOURCE_IGNORE_FILES "\\.git/;\\.github/;_build/;\\.git;\\.gitattributes;\\.gitignore;.*~\$")
|
||||
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698;/")
|
||||
set(CPACK_SOURCE_OUTPUT_CONFIG_FILE "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64/CPackSourceConfig.cmake")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "threadx-0.1.1-Source")
|
||||
set(CPACK_SOURCE_TOPLEVEL_TAG "Generic-Source")
|
||||
set(CPACK_STRIP_FILES "")
|
||||
set(CPACK_SYSTEM_NAME "Generic")
|
||||
set(CPACK_THREADS "1")
|
||||
set(CPACK_TOPLEVEL_TAG "Generic-Source")
|
||||
set(CPACK_VERBATIM_VARIABLES "YES")
|
||||
set(CPACK_WIX_SIZEOF_VOID_P "8")
|
||||
|
||||
if(NOT CPACK_PROPERTIES_FILE)
|
||||
set(CPACK_PROPERTIES_FILE "/tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64/CPackProperties.cmake")
|
||||
endif()
|
||||
|
||||
if(EXISTS ${CPACK_PROPERTIES_FILE})
|
||||
include(${CPACK_PROPERTIES_FILE})
|
||||
endif()
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,182 @@
|
||||
|
||||
file /tmp/claude-1000/-home-fdesbiens-652-threadx/3e4a51a5-e5ba-416c-90fd-fe90ff96f461/scratchpad/wt698/build-rv64/ports/risc-v64/gnu/example_build/qemu_virt/kernel.elf
|
||||
target remote :46936
|
||||
set pagination off
|
||||
set confirm off
|
||||
|
||||
# Setup Breakpoints
|
||||
break tx_application_define
|
||||
break thread_0_entry
|
||||
break thread_6_and_7_entry
|
||||
break _tx_timer_interrupt
|
||||
|
||||
# The timer interrupt fires at TX_TIMER_TICKS_PER_SECOND Hz. With every
|
||||
# breakpoint armed at once a bare `continue` is a race: a tick can steal
|
||||
# the stop that the script expects on an application breakpoint. Keep the
|
||||
# timer breakpoint disabled until the timer phase, so every stop below is
|
||||
# unambiguous at any tick rate.
|
||||
disable 4
|
||||
|
||||
# Execute to Application Definition
|
||||
continue
|
||||
|
||||
# Inspect mstatus once thread_0 has started
|
||||
continue
|
||||
print/x $mstatus
|
||||
|
||||
# Verify FPU Logic and Register State exercised by thread_6/7.
|
||||
# Only breakpoint 3 can stop us here, so this is thread_6_and_7_entry on
|
||||
# its first pass, before the thread has touched fpu_test_val.
|
||||
continue
|
||||
|
||||
# fpu_test_val exists only in a demo that builds the FPU exercise. GDB
|
||||
# aborts a sourced command file on an unknown symbol, which would skip
|
||||
# every later check without saying so, so probe for the symbol first. A
|
||||
# missing symbol still FAILS the FPU check below; it is not tolerated.
|
||||
python gdb.execute("set $fpu_sym = %d" % (1 if gdb.lookup_global_symbol("fpu_test_val") else 0))
|
||||
|
||||
set $fpu_iter = 0
|
||||
if $fpu_sym != 0
|
||||
set $fpu_before = fpu_test_val
|
||||
# Wait for the FPU add (fpu_test_val += 1.1f) to retire, instead of
|
||||
# stepping a fixed number of instructions past an assumed stop. Each
|
||||
# further stop is another pass through thread_6_and_7_entry, so the cap
|
||||
# bounds thread passes rather than ticks, and exhausting it fails below.
|
||||
while fpu_test_val == $fpu_before && $fpu_iter < 64
|
||||
set $fpu_iter = $fpu_iter + 1
|
||||
continue
|
||||
end
|
||||
end
|
||||
|
||||
print/x $mstatus
|
||||
info registers float
|
||||
|
||||
if $fpu_sym == 0
|
||||
printf "FPU_VERIFIED_FAIL_NO_SYMBOL\n"
|
||||
else
|
||||
print fpu_test_val
|
||||
# Assert the float unit produced an exact multiple of 1.1f. This does
|
||||
# not assume how many passes ran, only that every pass added 1.1f.
|
||||
set $fpu_n = (int)((fpu_test_val / 1.1) + 0.5)
|
||||
set $fpu_err = fpu_test_val - ($fpu_n * 1.1)
|
||||
if $fpu_n >= 1 && $fpu_err > -0.01 && $fpu_err < 0.01
|
||||
printf "FPU_VERIFIED_OK value=%f adds=%d\n", fpu_test_val, $fpu_n
|
||||
else
|
||||
printf "FPU_VERIFIED_FAIL value=%f adds=%d passes=%d\n", fpu_test_val, $fpu_n, $fpu_iter
|
||||
end
|
||||
end
|
||||
|
||||
# Await Timer Interrupt. Arm only the timer breakpoint, so this stop is
|
||||
# _tx_timer_interrupt and $ra below really is the ISR return address.
|
||||
disable 1
|
||||
disable 2
|
||||
disable 3
|
||||
enable 4
|
||||
continue
|
||||
print "Hit Timer Interrupt"
|
||||
|
||||
# Verify MEPC Integrity - Save State
|
||||
print/x $mepc
|
||||
set $saved_pc = $mepc
|
||||
|
||||
# Verify System Timer Before ISR
|
||||
set $clock_before = _tx_timer_system_clock
|
||||
print $clock_before
|
||||
|
||||
# Configure Time-Slice Test Conditions
|
||||
set _tx_timer_time_slice = 1
|
||||
set _tx_timer_expired_time_slice = 0
|
||||
set $ts_handler_called = 0
|
||||
|
||||
# Set Breakpoint at Time-Slice Handler with Auto-Continue
|
||||
tbreak _tx_thread_time_slice
|
||||
commands
|
||||
set $ts_handler_called = 1
|
||||
continue
|
||||
end
|
||||
|
||||
# Set Breakpoint at ISR Return Address. Disable the timer breakpoint for
|
||||
# this window, so the next tick cannot stop us short of the return.
|
||||
set $ret_addr = $ra
|
||||
disable 4
|
||||
tbreak *$ret_addr
|
||||
continue
|
||||
|
||||
# Verify that the live CSR and saved frame still contain the interrupted PC.
|
||||
set $frame_pc = ((unsigned long *)_tx_thread_current_ptr->tx_thread_stack_ptr)[30]
|
||||
if $mepc == $saved_pc && $frame_pc == $saved_pc
|
||||
printf "MEPC_VERIFIED_OK mepc=0x%lx frame_pc=0x%lx\n", $mepc, $frame_pc
|
||||
else
|
||||
printf "MEPC_VERIFIED_FAIL saved=0x%lx mepc=0x%lx frame_pc=0x%lx\n", $saved_pc, $mepc, $frame_pc
|
||||
end
|
||||
|
||||
# Verify Time-Slice Handler Was Called
|
||||
if $ts_handler_called == 1
|
||||
print "SUCCESS: Time-slice handler called."
|
||||
else
|
||||
print "FAILURE: Time-slice handler NOT called."
|
||||
end
|
||||
|
||||
# Verify System Timer Increment (Monotonicity)
|
||||
set $clock_after = _tx_timer_system_clock
|
||||
print $clock_after
|
||||
|
||||
if $clock_after > $clock_before
|
||||
print "SUCCESS: System timer incremented."
|
||||
else
|
||||
print "FAILURE: System timer did not increment."
|
||||
end
|
||||
|
||||
# Verify Preemption Logic (Thread Priority)
|
||||
#
|
||||
# We are now stopped at the return address from _tx_timer_interrupt,
|
||||
# after _tx_thread_time_slice and the timer expiration processing have had
|
||||
# a chance to update _tx_thread_execute_ptr but before trap_handler
|
||||
# returns into _tx_thread_context_restore.
|
||||
#
|
||||
# Whether any ONE tick lands inside a preemption window depends on
|
||||
# scheduling phase, not on the tick rate, so poll consecutive ticks at
|
||||
# this same sample point until the priority relation holds. Each retry
|
||||
# re-arms the time slice and re-derives the return address from $ra, so
|
||||
# the sample point is identical every iteration. The cap bounds ticks and
|
||||
# fails loudly on exhaustion; it never passes the check.
|
||||
set $preempt_ok = 0
|
||||
set $preempt_null = 0
|
||||
set $preempt_iter = 0
|
||||
while $preempt_iter < 250 && $preempt_ok == 0
|
||||
set $curr_ptr = _tx_thread_current_ptr
|
||||
set $exec_ptr = _tx_thread_execute_ptr
|
||||
if $curr_ptr == 0 || $exec_ptr == 0
|
||||
set $preempt_null = $preempt_null + 1
|
||||
else
|
||||
set $curr_prio = $curr_ptr->tx_thread_priority
|
||||
set $exec_prio = $exec_ptr->tx_thread_priority
|
||||
if $exec_prio < $curr_prio
|
||||
printf "PREEMPT_CHECK current_prio=%d execute_prio=%d\n", $curr_prio, $exec_prio
|
||||
set $preempt_ok = 1
|
||||
end
|
||||
end
|
||||
if $preempt_ok == 0
|
||||
set $preempt_iter = $preempt_iter + 1
|
||||
enable 4
|
||||
continue
|
||||
set _tx_timer_time_slice = 1
|
||||
set _tx_timer_expired_time_slice = 0
|
||||
set $ret_addr = $ra
|
||||
disable 4
|
||||
tbreak *$ret_addr
|
||||
continue
|
||||
end
|
||||
end
|
||||
|
||||
if $preempt_ok == 1
|
||||
printf "PREEMPT_VERIFIED_OK ticks=%d\n", $preempt_iter
|
||||
else
|
||||
if $preempt_null == $preempt_iter
|
||||
printf "PREEMPT_VERIFIED_FAIL_NULL\n"
|
||||
else
|
||||
printf "PREEMPT_VERIFIED_FAIL_NOT_OBSERVED ticks=%d\n", $preempt_iter
|
||||
end
|
||||
end
|
||||
|
||||
quit
|
||||
@@ -3,4 +3,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/threadx_riscv_port.cmake)
|
||||
threadx_add_riscv_port(
|
||||
SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src
|
||||
INC_DIR ${CMAKE_CURRENT_LIST_DIR}/inc
|
||||
EXAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/example_build/qemu_virt
|
||||
)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Ignore generated QEMU test files.
|
||||
kernel.elf
|
||||
qemu-riscv64.log
|
||||
test_cmds.gdb
|
||||
@@ -0,0 +1,51 @@
|
||||
set(QEMU_DEMO_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
add_executable(kernel.elf EXCLUDE_FROM_ALL
|
||||
${QEMU_DEMO_DIR}/demo_threadx.c
|
||||
${QEMU_DEMO_DIR}/entry.S
|
||||
${QEMU_DEMO_DIR}/uart.c
|
||||
${QEMU_DEMO_DIR}/plic.c
|
||||
${QEMU_DEMO_DIR}/hwtimer.c
|
||||
${QEMU_DEMO_DIR}/trap.c
|
||||
${QEMU_DEMO_DIR}/board.c
|
||||
${QEMU_DEMO_DIR}/tx_initialize_low_level.S
|
||||
)
|
||||
|
||||
target_link_libraries(kernel.elf PRIVATE threadx gcc)
|
||||
|
||||
target_include_directories(kernel.elf PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/common/inc
|
||||
${CMAKE_SOURCE_DIR}/ports/${THREADX_ARCH}/${THREADX_TOOLCHAIN}/inc
|
||||
${QEMU_DEMO_DIR}
|
||||
)
|
||||
|
||||
target_link_options(kernel.elf PRIVATE
|
||||
-T${QEMU_DEMO_DIR}/link.lds
|
||||
-nostartfiles
|
||||
-Wl,-Map=kernel.map
|
||||
)
|
||||
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
find_program(RISCV64_GDB NAMES riscv64-unknown-elf-gdb gdb-multiarch)
|
||||
find_program(RISCV64_QEMU NAMES qemu-system-riscv64)
|
||||
if(Python3_FOUND AND RISCV64_GDB AND RISCV64_QEMU)
|
||||
add_custom_target(check-functional-riscv64
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
${QEMU_DEMO_DIR}/test/threadx_test_tx_gnu_riscv64_qemu.py
|
||||
--elf $<TARGET_FILE:kernel.elf>
|
||||
--qemu ${RISCV64_QEMU}
|
||||
--gdb ${RISCV64_GDB}
|
||||
DEPENDS kernel.elf
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Running RISC-V64 QEMU/GDB functional test runner..."
|
||||
)
|
||||
elseif(NOT Python3_FOUND)
|
||||
message(STATUS
|
||||
"Python3 not found; check-functional-riscv64 target unavailable.")
|
||||
elseif(NOT RISCV64_GDB)
|
||||
message(STATUS
|
||||
"RISC-V GDB not found; check-functional-riscv64 target unavailable.")
|
||||
else()
|
||||
message(STATUS
|
||||
"qemu-system-riscv64 not found; check-functional-riscv64 target unavailable.")
|
||||
endif()
|
||||
@@ -15,15 +15,13 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void *memset(const void *des, int c,size_t n)
|
||||
void *memset(void *des, int c, size_t n)
|
||||
{
|
||||
if((des == NULL) || n <=0)
|
||||
return (void*)des;
|
||||
char* t = (char*)des;
|
||||
int i;
|
||||
for(i=0;i<n;i++)
|
||||
t[i]=c;
|
||||
return t;
|
||||
unsigned char *target = des;
|
||||
|
||||
for (size_t i = 0; i < n; i++)
|
||||
target[i] = (unsigned char)c;
|
||||
return des;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,26 +10,14 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
##############################################################################
|
||||
|
||||
printf "y\n" | rm -rf ../../../../../build/
|
||||
rm -f kernel.elf
|
||||
set -eu
|
||||
|
||||
pushd ../../../../../
|
||||
cmake -Bbuild -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/riscv64_gnu.cmake .
|
||||
cmake --build ./build/
|
||||
popd
|
||||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
REPO_ROOT=$(CDPATH= cd -- "${SCRIPT_DIR}/../../../../../" && pwd)
|
||||
BUILD_DIR=${BUILD_DIR:-"${REPO_ROOT}/build/riscv64-qemu"}
|
||||
|
||||
riscv64-unknown-elf-gcc \
|
||||
-march=rv64gc -mabi=lp64d \
|
||||
-mcmodel=medany -O0 -g3 -Wall \
|
||||
-ffunction-sections -fdata-sections \
|
||||
-I../../../../../common/inc \
|
||||
-I../../inc \
|
||||
entry.S \
|
||||
tx_initialize_low_level.S \
|
||||
board.c uart.c hwtimer.c plic.c trap.c demo_threadx.c \
|
||||
-L../../../../../build -lthreadx \
|
||||
-T link.lds -nostartfiles \
|
||||
-o kernel.elf
|
||||
cmake -S "${REPO_ROOT}" -B "${BUILD_DIR}" -GNinja \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${REPO_ROOT}/cmake/riscv64_gnu.cmake"
|
||||
cmake --build "${BUILD_DIR}" --target kernel.elf
|
||||
|
||||
|
||||
qemu-system-riscv64 -nographic -smp 1 -bios none -m 128M -machine virt -kernel kernel.elf
|
||||
printf 'Built %s\n' "${BUILD_DIR}/ports/risc-v64/gnu/example_build/qemu_virt/kernel.elf"
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define DEMO_BLOCK_POOL_SIZE 100
|
||||
#define DEMO_QUEUE_SIZE 100
|
||||
|
||||
float fpu_test_val = 0.0f;
|
||||
|
||||
/* Define the ThreadX object control blocks... */
|
||||
|
||||
@@ -357,6 +358,8 @@ UINT status;
|
||||
if (status != TX_SUCCESS)
|
||||
break;
|
||||
|
||||
/* FPU Test */
|
||||
fpu_test_val += 1.1f;
|
||||
/* Get the mutex again with suspension. This shows
|
||||
that an owning thread may retrieve the mutex it
|
||||
owns multiple times. */
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/***************************************************************************/
|
||||
|
||||
|
||||
.section .text
|
||||
.section .text.entry
|
||||
.align 4
|
||||
.global _start
|
||||
.extern main
|
||||
@@ -22,7 +22,10 @@ _start:
|
||||
bne t0, zero, 1f
|
||||
li x1, 0
|
||||
li x2, 0
|
||||
li x3, 0
|
||||
.option push
|
||||
.option norelax
|
||||
la gp, __global_pointer$ /* x3 = gp; norelax keeps this load absolute */
|
||||
.option pop
|
||||
li x4, 0
|
||||
li x5, 0
|
||||
li x6, 0
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/***************************************************************************/
|
||||
|
||||
#include "tx_port.h"
|
||||
#include "tx_api.h"
|
||||
#include "csr.h"
|
||||
#include "hwtimer.h"
|
||||
|
||||
@@ -17,20 +17,31 @@
|
||||
#define CLINT_TIME (CLINT+0xBFF8)
|
||||
#define CLINT_TIMECMP(hart_id) (CLINT+0x4000+8*(hart_id))
|
||||
|
||||
/* RV64: naturally aligned 64-bit MMIO accesses are single loads/stores;
|
||||
volatile keeps the compiler from caching or reordering them. */
|
||||
#define MTIME (*(volatile uint64_t *)CLINT_TIME)
|
||||
#define MTIMECMP(hart) (*(volatile uint64_t *)CLINT_TIMECMP(hart))
|
||||
|
||||
int hwtimer_init(void)
|
||||
{
|
||||
int hart = riscv_get_core();
|
||||
uint64_t time = *((uint64_t*)CLINT_TIME);
|
||||
*((uint64_t*)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER;
|
||||
return 0;
|
||||
int hart = riscv_get_core();
|
||||
|
||||
MTIMECMP(hart) = MTIME + TICKNUM_PER_TIMER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hwtimer_handler(void)
|
||||
{
|
||||
int hart = riscv_get_core();
|
||||
uint64_t time = *((uint64_t*)CLINT_TIME);
|
||||
*((uint64_t*)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER;
|
||||
return 0;
|
||||
}
|
||||
int hart = riscv_get_core();
|
||||
|
||||
/* Advance from the previous compare value, so trap latency
|
||||
does not accumulate as tick drift. */
|
||||
uint64_t next = MTIMECMP(hart) + TICKNUM_PER_TIMER;
|
||||
uint64_t now = MTIME;
|
||||
|
||||
if (next <= now)
|
||||
next = now + TICKNUM_PER_TIMER;
|
||||
|
||||
MTIMECMP(hart) = next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define TICKNUM_PER_SECOND 10000000
|
||||
#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 10)
|
||||
#define TICKNUM_PER_SECOND 10000000UL
|
||||
#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / TX_TIMER_TICKS_PER_SECOND)
|
||||
|
||||
int hwtimer_init(void);
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
OUTPUT_ARCH( "riscv" )
|
||||
ENTRY( _start )
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
data PT_LOAD FLAGS(6);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/*
|
||||
@@ -10,43 +16,44 @@ SECTIONS
|
||||
. = 0x80000000;
|
||||
|
||||
.text : {
|
||||
KEEP(*(.text.entry))
|
||||
*(.text .text.*)
|
||||
. = ALIGN(0x1000);
|
||||
PROVIDE(etext = .);
|
||||
}
|
||||
} :text
|
||||
|
||||
.rodata : {
|
||||
. = ALIGN(16);
|
||||
*(.srodata .srodata.*) /* do not need to distinguish this from .rodata */
|
||||
. = ALIGN(16);
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
} :text
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
.data : {
|
||||
. = ALIGN(16);
|
||||
PROVIDE( __global_pointer$ = . + 0x800 );
|
||||
*(.sdata .sdata.*) /* do not need to distinguish this from .data */
|
||||
. = ALIGN(16);
|
||||
*(.data .data.*)
|
||||
}
|
||||
} :data
|
||||
|
||||
.bss : {
|
||||
.bss (NOLOAD) : {
|
||||
. = ALIGN(16);
|
||||
_bss_start = .;
|
||||
*(.sbss .sbss.*) /* do not need to distinguish this from .bss */
|
||||
. = ALIGN(16);
|
||||
*(.bss .bss.*)
|
||||
_bss_end = .;
|
||||
}
|
||||
} :data
|
||||
|
||||
.stack : {
|
||||
.stack (NOLOAD) : {
|
||||
. = ALIGN(4096);
|
||||
_sysstack_start = .;
|
||||
. += 0x1000;
|
||||
#ifdef __riscv_vector
|
||||
. += 0x4000;
|
||||
#endif
|
||||
. += 0x4000; /* Vector headroom */
|
||||
_sysstack_end = .;
|
||||
}
|
||||
} :data
|
||||
|
||||
PROVIDE(_end = .);
|
||||
}
|
||||
|
||||
+184
-44
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "csr.h"
|
||||
#include "tx_port.h"
|
||||
|
||||
.section .text
|
||||
.align 4
|
||||
@@ -61,17 +62,18 @@
|
||||
.extern trap_handler
|
||||
.extern _tx_thread_context_restore
|
||||
trap_entry:
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
addi sp, sp, -520 // Allocate space for all registers - with floating point enabled (65*8)
|
||||
#else
|
||||
addi sp, sp, -256 // Allocate space for all registers - without floating point enabled (32*8)
|
||||
#endif
|
||||
addi sp, sp, -TX_RISCV_TRAP_FRAME_SIZE // Interrupt frame from the port contract (528 with FP, 256 without)
|
||||
#if defined(__riscv_vector)
|
||||
/* Allocate space for vector registers */
|
||||
/* Allocate space for vector registers. No register is saved yet, so
|
||||
park t4 in the frame first and recover it after the allocation:
|
||||
_tx_thread_context_save must still see the interrupted thread's t4. */
|
||||
sd t4, 15*8(sp) // Park t4 (pre-vector sp)
|
||||
csrr t4, vlenb
|
||||
slli t4, t4, 5
|
||||
addi t4, t4, 4*8
|
||||
sub sp, sp, t4
|
||||
add t4, sp, t4 // Rebuild the pre-vector sp
|
||||
ld t4, 15*8(t4) // Recover the parked t4
|
||||
#endif
|
||||
|
||||
sd x1, 224(sp) // Store RA (28*8 = 224, because call will override ra [ra is a callee register in riscv])
|
||||
@@ -81,11 +83,11 @@
|
||||
csrr a0, mcause
|
||||
csrr a1, mepc
|
||||
csrr a2, mtval
|
||||
addi sp, sp, -8
|
||||
addi sp, sp, -16 // 16 bytes keep sp 16-byte aligned at the call
|
||||
sd ra, 0(sp)
|
||||
call trap_handler
|
||||
ld ra, 0(sp)
|
||||
addi sp, sp, 8
|
||||
addi sp, sp, 16
|
||||
call _tx_thread_context_restore
|
||||
// it will nerver return
|
||||
_err:
|
||||
@@ -135,8 +137,7 @@ _err:
|
||||
/**************************************************************************/
|
||||
/* VOID _tx_initialize_low_level(VOID)
|
||||
{ */
|
||||
.global _tx_initialize_low_level
|
||||
.weak _tx_initialize_low_level
|
||||
.global _tx_initialize_low_level // Strong definition: must beat the port's weak default in any link order
|
||||
.extern _end
|
||||
.extern board_init
|
||||
_tx_initialize_low_level:
|
||||
@@ -150,9 +151,9 @@ _tx_initialize_low_level:
|
||||
csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit
|
||||
li t0, (MSTATUS_MPP_M | MSTATUS_MPIE )
|
||||
csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit
|
||||
li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE)
|
||||
csrrs zero, mie, t0 // set mie
|
||||
#ifdef __riscv_flen
|
||||
li t0, (MIE_MTIE | MIE_MEIE)
|
||||
csrrs zero, mie, t0 // set mie (no MIE_MSIE: cause 3 has no dispatch path and msip is never written)
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
li t0, MSTATUS_FS
|
||||
csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv
|
||||
fscsr x0
|
||||
@@ -161,11 +162,11 @@ _tx_initialize_low_level:
|
||||
li t0, MSTATUS_VS
|
||||
csrrs zero, mstatus, t0 // set MSTATUS_VS bit to open vector isa in riscv
|
||||
#endif
|
||||
addi sp, sp, -8
|
||||
addi sp, sp, -16 // 16 bytes keep sp 16-byte aligned at the call
|
||||
sd ra, 0(sp)
|
||||
call board_init
|
||||
ld ra, 0(sp)
|
||||
addi sp, sp, 8
|
||||
addi sp, sp, 16
|
||||
la t0, trap_entry
|
||||
csrw mtvec, t0
|
||||
ret
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
**************************************************************************/
|
||||
|
||||
// Some portions generated by Claude Code (Opus 5).
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
@@ -26,7 +28,7 @@
|
||||
/* PORT SPECIFIC C INFORMATION RELEASE */
|
||||
/* */
|
||||
/* tx_port.h RISC-V64/GNU */
|
||||
/* 6.2.1 */
|
||||
/* 6.5.1.202602a */
|
||||
/* */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
@@ -48,6 +50,48 @@
|
||||
#ifndef TX_PORT_H
|
||||
#define TX_PORT_H
|
||||
|
||||
#ifdef __riscv_float_abi_quad
|
||||
#error "The ThreadX RISC-V64 port does not support the LP64Q ABI."
|
||||
#endif
|
||||
|
||||
#if defined(__riscv_flen) && !defined(__riscv_float_abi_single) && !defined(__riscv_float_abi_double)
|
||||
#error "The ThreadX RISC-V64 port does not preserve FP state for a soft-float ABI. Remove F and D ISA extensions or use LP64F or LP64D."
|
||||
#endif
|
||||
|
||||
#if defined(__riscv_float_abi_single) && (!defined(__riscv_flen) || (__riscv_flen < 32))
|
||||
#error "The ThreadX RISC-V64 LP64F port requires FLEN>=32."
|
||||
#endif
|
||||
|
||||
#if defined(__riscv_float_abi_double) && (!defined(__riscv_flen) || (__riscv_flen < 64))
|
||||
#error "The ThreadX RISC-V64 LP64D port requires FLEN>=64."
|
||||
#endif
|
||||
|
||||
#if defined(__riscv_flen) && (__riscv_flen != 32) && (__riscv_flen != 64)
|
||||
#error "The ThreadX RISC-V64 port supports only FLEN=32 or FLEN=64."
|
||||
#endif
|
||||
|
||||
/* Every port .S file uses CSR instructions, so the ISA string must include
|
||||
Zicsr. Catch its absence here rather than in a wall of assembler
|
||||
"unrecognized opcode" errors. */
|
||||
#if !defined(__riscv_zicsr)
|
||||
#error "The ThreadX RISC-V64 port requires the Zicsr extension. Add it to the ISA string, for example rv64imac_zicsr."
|
||||
#endif
|
||||
|
||||
/* Publish the interrupt frame contract to GNU BSP assembly files. */
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
#define TX_RISCV_TRAP_FRAME_SIZE 528
|
||||
#else
|
||||
#define TX_RISCV_TRAP_FRAME_SIZE 256
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
#define TX_RISCV_SOL_FRAME_SIZE 240
|
||||
#else
|
||||
#define TX_RISCV_SOL_FRAME_SIZE 128
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
/* Include for memset. */
|
||||
@@ -62,6 +106,7 @@
|
||||
/* Yes, include the user defines in tx_user.h. The defines in this file may
|
||||
alternately be defined on the command line. */
|
||||
|
||||
|
||||
#include "tx_user.h"
|
||||
#endif /* TX_INCLUDE_USER_DEFINE_FILE */
|
||||
|
||||
@@ -90,10 +135,9 @@ typedef unsigned long long ULONG64;
|
||||
typedef short SHORT;
|
||||
typedef unsigned short USHORT;
|
||||
#define ULONG64_DEFINED
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
#define ALIGN_TYPE_DEFINED
|
||||
typedef unsigned long long ALIGN_TYPE;
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
/* On RV64, ULONG is 32-bit but pointers are 64-bit. Store the thread
|
||||
pointer in the timer's VOID * extension field so _tx_thread_timeout
|
||||
|
||||
@@ -13,6 +13,15 @@ Verify the toolchain:
|
||||
riscv64-unknown-elf-gcc --version
|
||||
riscv64-unknown-elf-objdump --version
|
||||
|
||||
ISA and ABI requirements
|
||||
- The ISA string must include Zicsr. rv64gc and rv64imafdc imply it;
|
||||
rv64imac does not, so write rv64imac_zicsr for an integer-only build.
|
||||
- Supported ABIs: lp64d (FLEN=64), lp64f (FLEN=32 or FLEN=64), and lp64
|
||||
only when the ISA has no F or D extension. lp64q is not supported.
|
||||
- The vector extension (V) is optional; the port saves the full vector
|
||||
state and sizes the frame from vlenb.
|
||||
- tx_port.h rejects every other combination at compile time.
|
||||
|
||||
CMake-based build (recommended)
|
||||
|
||||
From the ThreadX top-level directory:
|
||||
|
||||
@@ -79,10 +79,26 @@ _tx_initialize_low_level:
|
||||
|
||||
/* Initialize floating point control/status register if floating point is enabled. */
|
||||
#ifdef __riscv_flen
|
||||
#ifdef TX_RISCV_SMODE
|
||||
li t0, 0x2000 // Set sstatus.FS to Initial (bits 14:13 = 01) so the fcsr write cannot trap
|
||||
csrrs zero, sstatus, t0
|
||||
#else
|
||||
li t0, 0x2000 // Set mstatus.FS to Initial (bits 14:13 = 01) so the fcsr write cannot trap
|
||||
csrrs zero, mstatus, t0
|
||||
#endif
|
||||
li t0, 0
|
||||
csrw fcsr, t0 // Clear FP control/status register
|
||||
#endif
|
||||
|
||||
#ifdef __riscv_vector
|
||||
li t0, 0x200 // Set VS=Initial before a vector restore
|
||||
#ifdef TX_RISCV_SMODE
|
||||
csrrs zero, sstatus, t0
|
||||
#else
|
||||
csrrs zero, mstatus, t0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ret
|
||||
|
||||
/* Timer Interrupt Handler Note:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,8 @@
|
||||
/**************************************************************************/
|
||||
|
||||
.section .text
|
||||
#include "tx_port.h"
|
||||
.equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* FUNCTION RELEASE */
|
||||
@@ -68,14 +70,14 @@ _tx_thread_context_save:
|
||||
sd t1, 18*8(sp)
|
||||
|
||||
la t0, _tx_thread_system_state // Pickup address of system state
|
||||
ld t1, 0(t0) // Pickup system state
|
||||
lw t1, 0(t0) // Pickup system state
|
||||
|
||||
/* Check for a nested interrupt condition. */
|
||||
/* if (_tx_thread_system_state++)
|
||||
{ */
|
||||
beqz t1, _tx_thread_not_nested_save // If 0, first interrupt condition
|
||||
addi t1, t1, 1 // Increment the interrupt counter
|
||||
sd t1, 0(t0) // Store the interrupt counter
|
||||
sw t1, 0(t0) // Store the interrupt counter
|
||||
|
||||
/* Nested interrupt condition.
|
||||
Save the rest of the scratch registers on the stack and return to the
|
||||
@@ -103,7 +105,7 @@ _tx_thread_context_save:
|
||||
sd t0, 30*8(sp) // Save it on the stack
|
||||
|
||||
/* Save floating point scratch registers if floating point is enabled. */
|
||||
#ifdef __riscv_float_abi_single
|
||||
#if (__riscv_flen == 32)
|
||||
fsw f0, 31*8(sp) // Store ft0
|
||||
fsw f1, 32*8(sp) // Store ft1
|
||||
fsw f2, 33*8(sp) // Store ft2
|
||||
@@ -126,7 +128,7 @@ _tx_thread_context_save:
|
||||
fsw f31,62*8(sp) // Store ft11
|
||||
csrr t0, fcsr
|
||||
sd t0, 63*8(sp) // Store fcsr
|
||||
#elif defined(__riscv_float_abi_double)
|
||||
#elif (__riscv_flen == 64)
|
||||
fsd f0, 31*8(sp) // Store ft0
|
||||
fsd f1, 32*8(sp) // Store ft1
|
||||
fsd f2, 33*8(sp) // Store ft2
|
||||
@@ -135,6 +137,20 @@ _tx_thread_context_save:
|
||||
fsd f5, 36*8(sp) // Store ft5
|
||||
fsd f6, 37*8(sp) // Store ft6
|
||||
fsd f7, 38*8(sp) // Store ft7
|
||||
#if defined(__riscv_float_abi_single)
|
||||
fsd f8, 39*8(sp) // Store fs0
|
||||
fsd f9, 40*8(sp) // Store fs1
|
||||
fsd f18, 49*8(sp) // Store fs2
|
||||
fsd f19, 50*8(sp) // Store fs3
|
||||
fsd f20, 51*8(sp) // Store fs4
|
||||
fsd f21, 52*8(sp) // Store fs5
|
||||
fsd f22, 53*8(sp) // Store fs6
|
||||
fsd f23, 54*8(sp) // Store fs7
|
||||
fsd f24, 55*8(sp) // Store fs8
|
||||
fsd f25, 56*8(sp) // Store fs9
|
||||
fsd f26, 57*8(sp) // Store fs10
|
||||
fsd f27, 58*8(sp) // Store fs11
|
||||
#endif
|
||||
fsd f10,41*8(sp) // Store fa0
|
||||
fsd f11,42*8(sp) // Store fa1
|
||||
fsd f12,43*8(sp) // Store fa2
|
||||
@@ -198,7 +214,7 @@ _tx_thread_not_nested_save:
|
||||
/* else if (_tx_thread_current_ptr)
|
||||
{ */
|
||||
addi t1, t1, 1 // Increment the interrupt counter
|
||||
sd t1, 0(t0) // Store the interrupt counter
|
||||
sw t1, 0(t0) // Store the interrupt counter
|
||||
|
||||
/* Not nested: Find the user thread that was running and load our SP */
|
||||
|
||||
@@ -231,7 +247,7 @@ _tx_thread_not_nested_save:
|
||||
sd t1, 30*8(sp) // Save it on the stack
|
||||
|
||||
/* Save floating point scratch registers if floating point is enabled. */
|
||||
#ifdef __riscv_float_abi_single
|
||||
#if (__riscv_flen == 32)
|
||||
fsw f0, 31*8(sp) // Store ft0
|
||||
fsw f1, 32*8(sp) // Store ft1
|
||||
fsw f2, 33*8(sp) // Store ft2
|
||||
@@ -254,7 +270,7 @@ _tx_thread_not_nested_save:
|
||||
fsw f31,62*8(sp) // Store ft11
|
||||
csrr t0, fcsr
|
||||
sd t0, 63*8(sp) // Store fcsr
|
||||
#elif defined(__riscv_float_abi_double)
|
||||
#elif (__riscv_flen == 64)
|
||||
fsd f0, 31*8(sp) // Store ft0
|
||||
fsd f1, 32*8(sp) // Store ft1
|
||||
fsd f2, 33*8(sp) // Store ft2
|
||||
@@ -263,6 +279,20 @@ _tx_thread_not_nested_save:
|
||||
fsd f5, 36*8(sp) // Store ft5
|
||||
fsd f6, 37*8(sp) // Store ft6
|
||||
fsd f7, 38*8(sp) // Store ft7
|
||||
#if defined(__riscv_float_abi_single)
|
||||
fsd f8, 39*8(sp) // Store fs0
|
||||
fsd f9, 40*8(sp) // Store fs1
|
||||
fsd f18, 49*8(sp) // Store fs2
|
||||
fsd f19, 50*8(sp) // Store fs3
|
||||
fsd f20, 51*8(sp) // Store fs4
|
||||
fsd f21, 52*8(sp) // Store fs5
|
||||
fsd f22, 53*8(sp) // Store fs6
|
||||
fsd f23, 54*8(sp) // Store fs7
|
||||
fsd f24, 55*8(sp) // Store fs8
|
||||
fsd f25, 56*8(sp) // Store fs9
|
||||
fsd f26, 57*8(sp) // Store fs10
|
||||
fsd f27, 58*8(sp) // Store fs11
|
||||
#endif
|
||||
fsd f10,41*8(sp) // Store fa0
|
||||
fsd f11,42*8(sp) // Store fa1
|
||||
fsd f12,43*8(sp) // Store fa2
|
||||
@@ -351,18 +381,10 @@ _tx_thread_idle_system_save:
|
||||
|
||||
/* }
|
||||
} */
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
addi sp, sp, 65*8 // Recover stack frame - with floating point enabled
|
||||
#else
|
||||
addi sp, sp, 32*8 // Recover the reserved stack space
|
||||
#endif
|
||||
addi sp, sp, TX_TRAP_FRAME_SIZE // Recover stack frame - with floating point enabled (65*8 data + 8 pad)
|
||||
|
||||
#if defined(__riscv_vector)
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
addi t0, sp, -65*8
|
||||
#else
|
||||
addi t0, sp, -32*8
|
||||
#endif
|
||||
addi t0, sp, -TX_TRAP_FRAME_SIZE
|
||||
csrr t1, vlenb // Get vector register byte length
|
||||
slli t1, t1, 5 // Multiply by 32 (number of vector registers)
|
||||
addi t1, t1, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
/* FUNCTION RELEASE */
|
||||
/* */
|
||||
/* _tx_thread_interrupt_control RISC-V64/GNU */
|
||||
/* 6.2.1 */
|
||||
/* 6.5.1.202602a */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* Scott Larson, Microsoft Corporation */
|
||||
@@ -61,23 +61,25 @@ _tx_thread_interrupt_control:
|
||||
/* Pickup current interrupt lockout posture. */
|
||||
|
||||
#ifdef TX_RISCV_SMODE
|
||||
csrr t0, sstatus
|
||||
mv t1, t0 // Save original sstatus for return
|
||||
li t2, ~0x02 // Build mask to clear SIE (bit 1)
|
||||
and t0, t0, t2 // Clear SIE bit
|
||||
li t1, 0x02 // Build SIE bit mask (bit 1)
|
||||
andi a0, a0, 0x02 // Mask incoming to only SIE bit
|
||||
or t0, t0, a0 // Set requested SIE state
|
||||
csrw sstatus, t0
|
||||
andi a0, t1, 0x02 // Return original SIE bit
|
||||
bnez a0, 1f // If set, enable interrupts
|
||||
csrrc t0, sstatus, t1 // Atomically clear SIE, read old sstatus
|
||||
j 2f
|
||||
1:
|
||||
csrrs t0, sstatus, t1 // Atomically set SIE, read old sstatus
|
||||
2:
|
||||
andi a0, t0, 0x02 // Return original SIE bit
|
||||
#else
|
||||
csrr t0, mstatus
|
||||
mv t1, t0 // Save original mstatus for return
|
||||
li t2, ~0x08 // Build mask to clear MIE
|
||||
and t0, t0, t2 // Clear MIE bit
|
||||
li t1, 0x08 // Build MIE bit mask (bit 3)
|
||||
andi a0, a0, 0x08 // Mask incoming to only MIE bit
|
||||
or t0, t0, a0 // Set requested MIE state
|
||||
csrw mstatus, t0
|
||||
andi a0, t1, 0x08 // Return original MIE bit
|
||||
bnez a0, 1f // If set, enable interrupts
|
||||
csrrc t0, mstatus, t1 // Atomically clear MIE, read old mstatus
|
||||
j 2f
|
||||
1:
|
||||
csrrs t0, mstatus, t1 // Atomically set MIE, read old mstatus
|
||||
2:
|
||||
andi a0, t0, 0x08 // Return original MIE bit
|
||||
#endif
|
||||
ret
|
||||
/* } */
|
||||
|
||||
@@ -58,6 +58,9 @@
|
||||
/**************************************************************************/
|
||||
/* VOID _tx_thread_schedule(VOID)
|
||||
{ */
|
||||
#include "tx_port.h"
|
||||
.equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
|
||||
.equ TX_SOL_FRAME_SIZE, TX_RISCV_SOL_FRAME_SIZE
|
||||
.global _tx_thread_schedule
|
||||
_tx_thread_schedule:
|
||||
|
||||
@@ -142,7 +145,7 @@ _tx_thread_schedule_loop:
|
||||
|
||||
/* Determine if floating point registers need to be recovered. */
|
||||
|
||||
#if defined(__riscv_float_abi_single)
|
||||
#if (__riscv_flen == 32)
|
||||
flw f0, 31*8(sp) // Recover ft0
|
||||
flw f1, 32*8(sp) // Recover ft1
|
||||
flw f2, 33*8(sp) // Recover ft2
|
||||
@@ -177,7 +180,7 @@ _tx_thread_schedule_loop:
|
||||
flw f31,62*8(sp) // Recover ft11
|
||||
ld t0, 63*8(sp) // Recover fcsr
|
||||
csrw fcsr, t0 // Restore fcsr
|
||||
#elif defined(__riscv_float_abi_double)
|
||||
#elif (__riscv_flen == 64)
|
||||
fld f0, 31*8(sp) // Recover ft0
|
||||
fld f1, 32*8(sp) // Recover ft1
|
||||
fld f2, 33*8(sp) // Recover ft2
|
||||
@@ -258,6 +261,10 @@ _tx_thread_schedule_loop:
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
li t1, 0x6000 // Set FS=Dirty (bits 14:13)
|
||||
or t0, t0, t1
|
||||
#endif
|
||||
#if defined(__riscv_vector)
|
||||
li t1, 0x0600
|
||||
or t0, t0, t1
|
||||
#endif
|
||||
csrw sstatus, t0 // Update sstatus safely
|
||||
#else
|
||||
@@ -274,7 +281,7 @@ _tx_thread_schedule_loop:
|
||||
or t0, t0, t1
|
||||
#endif
|
||||
#if defined(__riscv_vector)
|
||||
li t1, 0x0200 // Set VS bits (bits 10:9 to 01) for vector state
|
||||
li t1, 0x0600
|
||||
or t0, t0, t1
|
||||
#endif
|
||||
csrw mstatus, t0 // Set mstatus
|
||||
@@ -309,17 +316,9 @@ _tx_thread_schedule_loop:
|
||||
ld t5, 14*8(sp) // Recover t5
|
||||
ld t6, 13*8(sp) // Recover t6
|
||||
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
addi sp, sp, 65*8 // Recover stack frame - with floating point registers
|
||||
#else
|
||||
addi sp, sp, 32*8 // Recover stack frame - without floating point registers
|
||||
#endif
|
||||
addi sp, sp, TX_TRAP_FRAME_SIZE // Recover stack frame - with floating point registers (65*8 data + 8 pad)
|
||||
#if defined(__riscv_vector)
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
addi t0, sp, -65*8
|
||||
#else
|
||||
addi t0, sp, -32*8
|
||||
#endif
|
||||
addi t0, sp, -TX_TRAP_FRAME_SIZE
|
||||
csrr t1, vlenb // Get vector register byte length
|
||||
slli t1, t1, 5 // Multiply by 32 (number of vector registers)
|
||||
addi t1, t1, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr
|
||||
@@ -336,7 +335,7 @@ _tx_thread_schedule_loop:
|
||||
|
||||
_tx_thread_synch_return:
|
||||
|
||||
#if defined(__riscv_float_abi_single)
|
||||
#if (__riscv_flen == 32)
|
||||
flw f8, 15*8(sp) // Recover fs0
|
||||
flw f9, 16*8(sp) // Recover fs1
|
||||
flw f18,17*8(sp) // Recover fs2
|
||||
@@ -351,7 +350,7 @@ _tx_thread_synch_return:
|
||||
flw f27,26*8(sp) // Recover fs11
|
||||
ld t0, 27*8(sp) // Recover fcsr
|
||||
csrw fcsr, t0 //
|
||||
#elif defined(__riscv_float_abi_double)
|
||||
#elif (__riscv_flen == 64)
|
||||
fld f8, 15*8(sp) // Recover fs0
|
||||
fld f9, 16*8(sp) // Recover fs1
|
||||
fld f18,17*8(sp) // Recover fs2
|
||||
@@ -418,11 +417,7 @@ _tx_thread_synch_return:
|
||||
#else
|
||||
csrw mstatus, t0 // Store mstatus, enables interrupt
|
||||
#endif
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
addi sp, sp, 29*8 // Recover stack frame
|
||||
#else
|
||||
addi sp, sp, 16*8 // Recover stack frame
|
||||
#endif
|
||||
addi sp, sp, TX_SOL_FRAME_SIZE // Recover stack frame (29*8 data + 8 pad)
|
||||
#if defined(__riscv_vector)
|
||||
csrr t1, vlenb // Get vector register byte length
|
||||
slli t1, t1, 5 // Multiply by 32 (number of vector registers)
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
/**************************************************************************/
|
||||
|
||||
.section .text
|
||||
#include "tx_port.h"
|
||||
.equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* FUNCTION RELEASE */
|
||||
@@ -138,6 +140,9 @@ If vector extension support:
|
||||
...
|
||||
v31 99 Initial v31
|
||||
|
||||
With floating point enabled, 8 pad bytes sit at the top of the frame
|
||||
(bytes 520-527) so the 528-byte total keeps sp 16-byte aligned.
|
||||
|
||||
Stack Bottom: (higher memory address) */
|
||||
|
||||
ld t0, 24(a0) // Pickup end of stack area
|
||||
@@ -146,11 +151,7 @@ If vector extension support:
|
||||
|
||||
/* Actually build the stack frame. */
|
||||
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
addi t0, t0, -65*8
|
||||
#else
|
||||
addi t0, t0, -32*8 // Allocate space for the stack frame
|
||||
#endif
|
||||
addi t0, t0, -TX_TRAP_FRAME_SIZE
|
||||
|
||||
#if defined(__riscv_vector)
|
||||
/* Vector extension support: calculate space based on vlenb */
|
||||
@@ -226,8 +227,7 @@ If vector extension support:
|
||||
sd zero, 60*8(t0) // Initial ft9
|
||||
sd zero, 61*8(t0) // Initial ft10
|
||||
sd zero, 62*8(t0) // Initial ft11
|
||||
csrr a1, fcsr // Read fcsr for initial value
|
||||
sd a1, 63*8(t0) // Initial fcsr
|
||||
sd zero, 63*8(t0) // Initial fcsr (0: no sticky flags, round-to-nearest)
|
||||
#endif
|
||||
|
||||
#if defined(__riscv_vector)
|
||||
|
||||
@@ -57,17 +57,15 @@
|
||||
/**************************************************************************/
|
||||
/* VOID _tx_thread_system_return(VOID)
|
||||
{ */
|
||||
#include "tx_port.h"
|
||||
.equ TX_SOL_FRAME_SIZE, TX_RISCV_SOL_FRAME_SIZE
|
||||
.global _tx_thread_system_return
|
||||
_tx_thread_system_return:
|
||||
|
||||
/* Save minimal context on the stack. */
|
||||
/* sp -= sizeof(stack_frame); */
|
||||
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
addi sp, sp, -29*8 // Allocate space on the stack - with floating point enabled
|
||||
#else
|
||||
addi sp, sp, -16*8 // Allocate space on the stack - without floating point enabled
|
||||
#endif
|
||||
addi sp, sp, -TX_SOL_FRAME_SIZE // Allocate space on the stack
|
||||
#if defined(__riscv_vector)
|
||||
csrr t1, vlenb // Get vector register byte length
|
||||
slli t1, t1, 5 // Multiply by 32 (number of vector registers)
|
||||
@@ -76,7 +74,7 @@ _tx_thread_system_return:
|
||||
#endif
|
||||
|
||||
/* Store floating point preserved registers. */
|
||||
#if defined(__riscv_float_abi_single)
|
||||
#if (__riscv_flen == 32)
|
||||
fsw f8, 15*8(sp) // Store fs0
|
||||
fsw f9, 16*8(sp) // Store fs1
|
||||
fsw f18, 17*8(sp) // Store fs2
|
||||
@@ -91,7 +89,7 @@ _tx_thread_system_return:
|
||||
fsw f27, 26*8(sp) // Store fs11
|
||||
csrr t0, fcsr
|
||||
sd t0, 27*8(sp) // Store fcsr
|
||||
#elif defined(__riscv_float_abi_double)
|
||||
#elif (__riscv_flen == 64)
|
||||
fsd f8, 15*8(sp) // Store fs0
|
||||
fsd f9, 16*8(sp) // Store fs1
|
||||
fsd f18, 17*8(sp) // Store fs2
|
||||
|
||||
@@ -88,7 +88,7 @@ _tx_timer_interrupt:
|
||||
/* Check for expiration. */
|
||||
/* if (_tx_timer_time_slice == 0) */
|
||||
|
||||
bgtz t3, _tx_timer_no_time_slice // If not 0, has not expired yet
|
||||
bnez t3, _tx_timer_no_time_slice // If not 0, has not expired yet
|
||||
li t1, 1 // Build expired flag
|
||||
|
||||
/* Set the time-slice expired flag. */
|
||||
@@ -181,7 +181,7 @@ _tx_timer_dont_activate:
|
||||
/* if (_tx_timer_expired_time_slice)
|
||||
{ */
|
||||
|
||||
andi t2, t6, 1 // Is the timer expired bit set?
|
||||
andi t2, t6, 1 // Is the time-slice expired bit set?
|
||||
beqz t2, _tx_timer_not_ts_expiration // If not, skip time slice processing
|
||||
|
||||
/* Time slice interrupted thread. */
|
||||
|
||||
@@ -9,21 +9,42 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
**************************************************************************/
|
||||
|
||||
/* PLIC driver for the QEMU virt machine (RV32 and RV64),
|
||||
M-mode context. */
|
||||
|
||||
#include "plic.h"
|
||||
#include <stddef.h>
|
||||
|
||||
irq_callback callbacks[MAX_CALLBACK_NUM];
|
||||
|
||||
/* Enable word that holds source irqno for this hart's M-mode context. */
|
||||
static volatile uint32_t *plic_enable_word(int hart, int irqno)
|
||||
{
|
||||
return (volatile uint32_t *)(PLIC_MENABLE(hart) + ((unsigned int)irqno / 32u) * 4u);
|
||||
}
|
||||
|
||||
void plic_irq_enable(int irqno)
|
||||
{
|
||||
int hart = riscv_get_core();
|
||||
*(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) | (1 << irqno));
|
||||
volatile uint32_t *reg;
|
||||
|
||||
if ((irqno <= 0) || (irqno >= MAX_CALLBACK_NUM))
|
||||
return;
|
||||
|
||||
reg = plic_enable_word(hart, irqno);
|
||||
*reg = *reg | (1u << ((unsigned int)irqno % 32u));
|
||||
}
|
||||
|
||||
void plic_irq_disable(int irqno)
|
||||
{
|
||||
int hart = riscv_get_core();
|
||||
*(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) & (~(1 << irqno)));
|
||||
volatile uint32_t *reg;
|
||||
|
||||
if ((irqno <= 0) || (irqno >= MAX_CALLBACK_NUM))
|
||||
return;
|
||||
|
||||
reg = plic_enable_word(hart, irqno);
|
||||
*reg = *reg & ~(1u << ((unsigned int)irqno % 32u));
|
||||
}
|
||||
|
||||
void plic_prio_set(int irqno, int prio)
|
||||
@@ -33,7 +54,7 @@ void plic_prio_set(int irqno, int prio)
|
||||
|
||||
int plic_prio_get(int irqno)
|
||||
{
|
||||
return PLIC_GET_PRIO(irqno);
|
||||
return (int)PLIC_GET_PRIO(irqno);
|
||||
}
|
||||
|
||||
int plic_register_callback(int irqno, irq_callback callback)
|
||||
@@ -51,29 +72,51 @@ int plic_unregister_callback(int irqno)
|
||||
|
||||
int plic_init(void)
|
||||
{
|
||||
int hart = riscv_get_core();
|
||||
|
||||
for (int i = 0; i < MAX_CALLBACK_NUM; i++)
|
||||
callbacks[i] = NULL;
|
||||
|
||||
/* Do not depend on the reset state or on a prior boot stage: accept
|
||||
every priority (threshold 0) and start with all sources for this
|
||||
hart disabled. Drivers enable their own sources. */
|
||||
*(volatile uint32_t *)PLIC_MPRIORITY(hart) = 0;
|
||||
for (int w = 0; w < MAX_CALLBACK_NUM / 32; w++)
|
||||
*(volatile uint32_t *)(PLIC_MENABLE(hart) + (unsigned int)w * 4u) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plic_claim(void)
|
||||
{
|
||||
int hart = riscv_get_core();
|
||||
return (*(uint32_t*)PLIC_MCLAIM(hart));
|
||||
return (int)(*(volatile uint32_t *)PLIC_MCLAIM(hart));
|
||||
}
|
||||
|
||||
void plic_complete(int irqno)
|
||||
{
|
||||
int hart = riscv_get_core();
|
||||
*(uint32_t*)(PLIC_MCOMPLETE(hart)) = (uint32_t)irqno;
|
||||
*(volatile uint32_t *)(PLIC_MCOMPLETE(hart)) = (uint32_t)irqno;
|
||||
}
|
||||
|
||||
int plic_irq_intr(void)
|
||||
{
|
||||
int ret = -1;
|
||||
int irqno = plic_claim();
|
||||
|
||||
if (irqno == 0)
|
||||
return 0;
|
||||
|
||||
if ((irqno < 0) || (irqno >= MAX_CALLBACK_NUM))
|
||||
{
|
||||
if (irqno > 0)
|
||||
plic_complete(irqno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (callbacks[irqno] != NULL)
|
||||
ret = (callbacks[irqno])(irqno);
|
||||
|
||||
plic_complete(irqno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#define PLIC_MCOMPLETE(hart) (PLIC + 0x200004 + (hart)*0x2000)
|
||||
#define PLIC_SCOMPLETE(hart) (PLIC + 0x201004 + (hart)*0x2000)
|
||||
|
||||
#define PLIC_GET_PRIO(irqno) (*(uint32_t *)(PLIC_PRIORITY + (irqno)*4))
|
||||
#define PLIC_SET_PRIO(irqno, prio) (*(uint32_t *)(PLIC_PRIORITY + (irqno)*4) = (prio))
|
||||
#define PLIC_GET_PRIO(irqno) (*(volatile uint32_t *)(PLIC_PRIORITY + (irqno)*4))
|
||||
#define PLIC_SET_PRIO(irqno, prio) (*(volatile uint32_t *)(PLIC_PRIORITY + (irqno)*4) = (prio))
|
||||
|
||||
#define MAX_CALLBACK_NUM 128
|
||||
typedef int (*irq_callback)(int irqno);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* mcause constants use __riscv_xlen to resolve correctly for both ISAs:
|
||||
* RV32: interrupt bit = bit 31 (0x80000000)
|
||||
* RV64: interrupt bit = bit 63 (0x8000000000000000)
|
||||
*
|
||||
*/
|
||||
|
||||
#include "csr.h"
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "plic.h"
|
||||
#include <tx_port.h>
|
||||
#include <tx_api.h>
|
||||
#include <tx_thread.h>
|
||||
|
||||
#define MCAUSE_INT_BIT ((uintptr_t)1 << (__riscv_xlen - 1))
|
||||
|
||||
@@ -30,11 +32,21 @@
|
||||
#define OS_IS_TICK_INT(mcause) ((mcause) == (MCAUSE_INT_BIT | 7u))
|
||||
#define OS_IS_SOFT_INT(mcause) ((mcause) == (MCAUSE_INT_BIT | 3u))
|
||||
#define OS_IS_EXT_INT(mcause) ((mcause) == (MCAUSE_INT_BIT | 11u))
|
||||
#define OS_IS_TRAP_USER(mcause) ((mcause) == 11u)
|
||||
|
||||
/* Synchronous exception codes (interrupt bit clear), privileged spec table
|
||||
"Machine cause register (mcause) values after trap". */
|
||||
#define CAUSE_BREAKPOINT 3u
|
||||
#define CAUSE_ECALL_U 8u
|
||||
#define CAUSE_ECALL_S 9u
|
||||
#define CAUSE_ECALL_M 11u
|
||||
|
||||
/* Saved mepc lives in slot 30 of the interrupt frame that the trap entry
|
||||
and _tx_thread_context_save build. One slot is XLEN/8 bytes on both
|
||||
ports, so a uintptr_t index works for RV32 and RV64. */
|
||||
#define TX_RISCV_FRAME_MEPC_SLOT 30
|
||||
|
||||
extern void _tx_timer_interrupt(void);
|
||||
|
||||
#ifdef TX_RISCV_TRAP_DEBUG
|
||||
static void print_hex(uintptr_t val)
|
||||
{
|
||||
const char digits[] = "0123456789ABCDEF";
|
||||
@@ -42,12 +54,29 @@ static void print_hex(uintptr_t val)
|
||||
uart_putc('x');
|
||||
for (int i = (int)(sizeof(uintptr_t) * 2) - 1; i >= 0; i--)
|
||||
{
|
||||
int d = (val >> (i * 4)) & 0xF;
|
||||
int d = (int)((val >> (i * 4)) & 0xFu);
|
||||
uart_putc(digits[d]);
|
||||
}
|
||||
uart_putc('\n');
|
||||
}
|
||||
#endif /* TX_RISCV_TRAP_DEBUG */
|
||||
|
||||
static int trap_skip_instruction(uintptr_t mepc)
|
||||
{
|
||||
TX_THREAD *thread_ptr = _tx_thread_current_ptr;
|
||||
uintptr_t *frame;
|
||||
uintptr_t length;
|
||||
|
||||
if ((TX_THREAD_GET_SYSTEM_STATE() != 1u) || (thread_ptr == TX_NULL))
|
||||
return -1;
|
||||
|
||||
/* A 32-bit instruction has its two low bits set; a compressed one does
|
||||
not. ecall and ebreak are 32-bit, c.ebreak is 16-bit. */
|
||||
length = ((*(volatile uint16_t *)mepc & 3u) == 3u) ? 4u : 2u;
|
||||
|
||||
frame = (uintptr_t *)thread_ptr->tx_thread_stack_ptr;
|
||||
frame[TX_RISCV_FRAME_MEPC_SLOT] = mepc + length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval)
|
||||
{
|
||||
@@ -63,30 +92,42 @@ void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval)
|
||||
int ret = plic_irq_intr();
|
||||
if (ret)
|
||||
{
|
||||
puts("[INTERRUPT]: handler irq error!");
|
||||
uart_puts("[INTERRUPT]: handler irq error!");
|
||||
while (1) ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("[INTERRUPT]: now can't deal with the interrupt!");
|
||||
uart_puts("[INTERRUPT]: unhandled interrupt, halting");
|
||||
uart_puts("mcause:");
|
||||
print_hex(mcause);
|
||||
while (1) ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("[EXCEPTION] : Unknown Error!!");
|
||||
#ifdef TX_RISCV_TRAP_DEBUG
|
||||
puts("mcause:");
|
||||
uart_puts("[EXCEPTION]");
|
||||
uart_puts("mcause:");
|
||||
print_hex(mcause);
|
||||
puts("mepc:");
|
||||
uart_puts("mepc:");
|
||||
print_hex(mepc);
|
||||
puts("mtval:");
|
||||
uart_puts("mtval:");
|
||||
print_hex(mtval);
|
||||
#else
|
||||
(void)mepc;
|
||||
(void)mtval;
|
||||
#endif /* TX_RISCV_TRAP_DEBUG */
|
||||
|
||||
if ((mcause == CAUSE_BREAKPOINT) || (mcause == CAUSE_ECALL_U) ||
|
||||
(mcause == CAUSE_ECALL_S) || (mcause == CAUSE_ECALL_M))
|
||||
{
|
||||
if (trap_skip_instruction(mepc) == 0)
|
||||
{
|
||||
uart_puts("[EXCEPTION]: ecall/ebreak skipped, resuming");
|
||||
return;
|
||||
}
|
||||
uart_puts("[EXCEPTION]: ecall/ebreak outside a thread, halting");
|
||||
}
|
||||
else
|
||||
{
|
||||
uart_puts("[EXCEPTION]: unhandled exception, halting");
|
||||
}
|
||||
while (1) ;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user