mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
Added lazy FPU stacking and QEMU functional tests for RV64 GNU port (#549)
* add lazy FPU stacking to context save/restore Save mstatus/sstatus to stack slot 29 and skip floating-point register save/restore when FS is Off (bits 14:13). This avoids unnecessary FP context work for threads that do not use the FPU. - context_save: check FS in nested and first-level interrupt paths - context_restore: gate FP restore on nested, no-preempt, and preempt paths - use sstatus when TX_RISCV_SMODE is defined, otherwise mstatus * add QEMU virt CMake build and automated test runner Wire the QEMU virt demo into the CMake build system and add a Python/GDB functional test runner, mirroring the risc-v32/gnu port. - Add qemu_virt/CMakeLists.txt to build kernel.elf and register the check-functional-riscv64 target (requires Python3; skipped if absent) - Link kernel.elf with --whole-archive so all ThreadX symbols resolve - Pin _start at 0x80000000 via .text.boot in entry.s and KEEP(*(.text.boot)) in link.lds - Extend demo_threadx.c with fpu_test_val and shorten thread_0 sleep for GDB-driven FPU, timer, and preemption checks - Add test/azrtos_test_tx_gnu_riscv64_qemu.py; verified passing on QEMU virt (FPU, timer interrupt, preemption) * Clean up RV64 PR scope and remove QEMU test integration leftovers Revert accidental RV64 qemu_virt test/CMake integration changes and keep this branch focused on lazy FPU context handling only. Also remove unintended TX_RISCV_SMODE-based mstatus/sstatus save path and align comments/logic to mstatus-only behavior. * Initialize mstatus.FS in RV64 stack build so new threads start with clean FP state Slot 29 was left uninitialized while context restore reads it as an FP-live hint; garbage FS bits could make a new thread inherit the previous thread's floating-point registers. * Add RV64 regression test for the FP state of a newly created thread The test dirties every floating point register, then creates a thread and checks that the stack builder wrote the mstatus slot and that the new thread starts with all floating point registers zeroed. It is registered for RV64 only, since the RV32 stack builder still leaves the slot unwritten. * Completed the RISC-V64 lazy FPU so the restore side matches the save side The lazy FPU save in this branch skips the floating-point stores when mstatus.FS is Off, and records the mstatus it judged that on in frame slot 29. Merged onto current dev, only the save side had that treatment: both restore paths and the scheduler's interrupt-frame path still reloaded the FP registers unconditionally, from slots the save had deliberately left alone. A thread that never touched the FP unit would have had whatever the frame happened to contain loaded into its registers, and FS driven to Dirty on the way out. The guard is added at the three places that consume an interrupt frame: both paths in _tx_thread_context_restore, and _tx_thread_schedule_loop. Each reads slot 29 and skips the FP block when FS was Off, which is the same shape the risc-v32 port already uses. The solicited path is deliberately left alone. _tx_thread_system_return saves the callee-saved FP registers unconditionally, so restoring them unconditionally is consistent; making that pair lazy as well is a separate change, and risc-v32 is the model for it. Verified with QEMU on all five configurations: risc-v64 96 of 96 passing, five configurations, nothing unlinkable risc-v32 95 of 95 passing, five configurations, unchanged functional check-functional-riscv64 passes every check The ninety-sixth test is the one this branch adds. It is load bearing: seeding stack build with FS = Off instead of Initial makes it fail, and restoring the seed makes it pass, so it guards the behaviour the rest of this branch is about rather than passing regardless. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> --------- Co-authored-by: r <r@r> Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
This commit is contained in:
co-authored by
r
Frédéric Desbiens
parent
16297d7a2a
commit
850a172bac
@@ -90,6 +90,16 @@ _tx_thread_context_restore:
|
||||
/* Just recover the saved registers and return to the point of
|
||||
interrupt. */
|
||||
|
||||
/* Recover the FP state only when the saved mstatus.FS was not Off. The
|
||||
save side skips the FP stores for a thread that has not touched the
|
||||
unit, so those slots hold nothing worth loading back. */
|
||||
#if (__riscv_flen == 32) || (__riscv_flen == 64)
|
||||
ld t1, 29*8(sp) // Pickup saved mstatus
|
||||
srli t1, t1, 13
|
||||
andi t1, t1, 0x3
|
||||
beqz t1, _tx_thread_skip_fp_restore // Skip if FS was Off
|
||||
#endif
|
||||
|
||||
/* Recover floating point registers. */
|
||||
#if (__riscv_flen == 32)
|
||||
flw f0, 31*8(sp) // Recover ft0
|
||||
@@ -152,6 +162,7 @@ _tx_thread_context_restore:
|
||||
ld t0, 63*8(sp) // Recover fcsr
|
||||
csrw fcsr, t0
|
||||
#endif
|
||||
_tx_thread_skip_fp_restore:
|
||||
|
||||
#if defined(__riscv_vector)
|
||||
/* Recover vector registers v0-v31 */
|
||||
@@ -297,6 +308,16 @@ _tx_thread_no_preempt_restore:
|
||||
|
||||
ld sp, 8(t1) // Switch back to thread's stack
|
||||
|
||||
/* Recover the FP state only when the saved mstatus.FS was not Off. The
|
||||
save side skips the FP stores for a thread that has not touched the
|
||||
unit, so those slots hold nothing worth loading back. */
|
||||
#if (__riscv_flen == 32) || (__riscv_flen == 64)
|
||||
ld t3, 29*8(sp) // Pickup saved mstatus
|
||||
srli t3, t3, 13
|
||||
andi t3, t3, 0x3
|
||||
beqz t3, _tx_thread_no_preempt_skip_fp_restore // Skip if FS was Off
|
||||
#endif
|
||||
|
||||
/* Recover floating point registers. */
|
||||
#if (__riscv_flen == 32)
|
||||
flw f0, 31*8(sp) // Recover ft0
|
||||
@@ -359,6 +380,7 @@ _tx_thread_no_preempt_restore:
|
||||
ld t0, 63*8(sp) // Recover fcsr
|
||||
csrw fcsr, t0 // Restore fcsr
|
||||
#endif
|
||||
_tx_thread_no_preempt_skip_fp_restore:
|
||||
|
||||
#if defined(__riscv_vector)
|
||||
/* Recover vector registers v0-v31 */
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* Scott Larson, Microsoft Corporation */
|
||||
/* Wei-Chen Lai, National Cheng Kung University */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
@@ -104,6 +105,15 @@ _tx_thread_context_save:
|
||||
#endif
|
||||
sd t0, 30*8(sp) // Save it on the stack
|
||||
|
||||
/* Save mstatus and skip FP state if FS is Off. */
|
||||
csrr t0, mstatus
|
||||
sd t0, 29*8(sp) // Save mstatus
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
srli t1, t0, 13
|
||||
andi t1, t1, 0x3
|
||||
beqz t1, _tx_thread_skip_nested_fpu_save // Skip if FS was Off
|
||||
#endif
|
||||
|
||||
/* Save floating point scratch registers if floating point is enabled. */
|
||||
#if (__riscv_flen == 32)
|
||||
fsw f0, 31*8(sp) // Store ft0
|
||||
@@ -166,6 +176,7 @@ _tx_thread_context_save:
|
||||
csrr t0, fcsr
|
||||
sd t0, 63*8(sp) // Store fcsr
|
||||
#endif
|
||||
_tx_thread_skip_nested_fpu_save:
|
||||
|
||||
#if defined(__riscv_vector)
|
||||
/* Store vector registers and CSRs */
|
||||
@@ -246,6 +257,15 @@ _tx_thread_not_nested_save:
|
||||
#endif
|
||||
sd t1, 30*8(sp) // Save it on the stack
|
||||
|
||||
/* Save mstatus and skip FP state if FS is Off. */
|
||||
csrr t1, mstatus
|
||||
sd t1, 29*8(sp) // Save mstatus
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
srli t2, t1, 13
|
||||
andi t2, t2, 0x3
|
||||
beqz t2, _tx_thread_skip_fpu_save // Skip if FS was Off
|
||||
#endif
|
||||
|
||||
/* Save floating point scratch registers if floating point is enabled. */
|
||||
#if (__riscv_flen == 32)
|
||||
fsw f0, 31*8(sp) // Store ft0
|
||||
@@ -308,6 +328,7 @@ _tx_thread_not_nested_save:
|
||||
csrr t0, fcsr
|
||||
sd t0, 63*8(sp) // Store fcsr
|
||||
#endif
|
||||
_tx_thread_skip_fpu_save:
|
||||
|
||||
#if defined(__riscv_vector)
|
||||
/* Store vector registers and CSRs */
|
||||
|
||||
@@ -143,7 +143,16 @@ _tx_thread_schedule_loop:
|
||||
ld t2, 0(sp) // Pickup stack type
|
||||
beqz t2, _tx_thread_synch_return // If 0, solicited thread return
|
||||
|
||||
/* Determine if floating point registers need to be recovered. */
|
||||
/* Determine if floating point registers need to be recovered. This is an
|
||||
interrupt frame, so _tx_thread_context_save built it and may have
|
||||
skipped the FP stores when mstatus.FS was Off. Read back the same slot
|
||||
it wrote rather than reloading whatever those slots happen to hold. */
|
||||
#if (__riscv_flen == 32) || (__riscv_flen == 64)
|
||||
ld t1, 29*8(sp) // Pickup saved mstatus
|
||||
srli t1, t1, 13
|
||||
andi t1, t1, 0x3
|
||||
beqz t1, _tx_thread_schedule_skip_fp_restore // Skip if FS was Off
|
||||
#endif
|
||||
|
||||
#if (__riscv_flen == 32)
|
||||
flw f0, 31*8(sp) // Recover ft0
|
||||
@@ -216,6 +225,7 @@ _tx_thread_schedule_loop:
|
||||
ld t0, 63*8(sp) // Recover fcsr
|
||||
csrw fcsr, t0 // Restore fcsr
|
||||
#endif
|
||||
_tx_thread_schedule_skip_fp_restore:
|
||||
|
||||
#if defined(__riscv_vector)
|
||||
/* Recover vector registers v0-v31 */
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* Scott Larson, Microsoft Corporation */
|
||||
/* Wei-Chen Lai, National Cheng Kung University */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
@@ -94,7 +95,7 @@ _tx_thread_stack_build:
|
||||
x11 26 Initial a1
|
||||
x10 27 Initial a0
|
||||
x1 28 Initial ra
|
||||
-- 29 reserved
|
||||
mstatus 29 Initial mstatus
|
||||
mepc 30 Initial mepc
|
||||
If floating point support:
|
||||
f0 31 Inital ft0
|
||||
@@ -193,6 +194,8 @@ If vector extension support:
|
||||
sd zero, 26*8(t0) // Initial a1
|
||||
sd zero, 27*8(t0) // Initial a0
|
||||
sd zero, 28*8(t0) // Initial ra
|
||||
li t1, 0x2000 // mstatus.FS = Initial
|
||||
sd t1, 29*8(t0) // Initial mstatus
|
||||
sd a1, 30*8(t0) // Initial mepc/sepc (thread entry point)
|
||||
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
|
||||
sd zero, 31*8(t0) // Initial ft0
|
||||
|
||||
@@ -115,6 +115,14 @@ set(regression_test_cases
|
||||
# interrupt_save on RISC-V.
|
||||
)
|
||||
|
||||
# Architecture specific tests living next to this file. The RV32 stack
|
||||
# builder still leaves the mstatus slot of the frame unwritten, so the new
|
||||
# thread FPU state test only applies to RV64 for now.
|
||||
if(THREADX_ARCH STREQUAL "risc-v64")
|
||||
list(APPEND regression_test_cases
|
||||
${CMAKE_CURRENT_LIST_DIR}/threadx_riscv_new_thread_fpu_state_test.c)
|
||||
endif()
|
||||
|
||||
# Tests that provide their own main() and must NOT link with testcontrol.
|
||||
set(standalone_test_cases
|
||||
${SOURCE_DIR}/threadx_initialize_kernel_setup_test.c
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user