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:
Akif Ejaz
2026-09-09 09:26:11 -04:00
committed by GitHub
co-authored by Frédéric Desbiens
parent 515ab8aba1
commit 164f211a01
34 changed files with 5867 additions and 264 deletions
Binary file not shown.
File diff suppressed because it is too large Load Diff
+76
View File
@@ -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()
+83
View File
@@ -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
+1
View File
@@ -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 = .);
}
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
+47 -3
View File
@@ -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
+9
View File
@@ -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
+39 -17
View File
@@ -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
/* } */
+15 -20
View File
@@ -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
+2 -2
View File
@@ -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. */
+48 -5
View File
@@ -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) ;
}
}