summaryrefslogtreecommitdiff
path: root/ports/risc-v32/gnu
diff options
context:
space:
mode:
authorFrédéric Desbiens <[email protected]>2026-10-02 10:42:44 -0400
committerGitHub <[email protected]>2026-10-02 10:42:44 -0400
commit93387b0a60382a29579bbad5045c68328e3751df (patch)
tree42be790d602cb76f55090e5a7c418df7ea2b3428 /ports/risc-v32/gnu
parent44d7c95c582d415c4ad84527180b29c93c3bf664 (diff)
parent28281cd5647d4671236ca08d19bd18ed15b2265a (diff)
Merge pull request #783 from eclipse-threadx/devv6.5.2.202603_rel
Merged changes ahead of the v6.5.2.202603 release
Diffstat (limited to 'ports/risc-v32/gnu')
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/.gitignore4
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt24
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/board.c14
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c4
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.c69
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.h4
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/link.lds21
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py277
-rw-r--r--ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S21
-rw-r--r--ports/risc-v32/gnu/inc/tx_port.h44
-rw-r--r--ports/risc-v32/gnu/src/tx_initialize_low_level.S9
-rw-r--r--ports/risc-v32/gnu/src/tx_thread_context_restore.S205
-rw-r--r--ports/risc-v32/gnu/src/tx_thread_context_save.S168
-rw-r--r--ports/risc-v32/gnu/src/tx_thread_schedule.S171
-rw-r--r--ports/risc-v32/gnu/src/tx_thread_stack_build.S104
-rw-r--r--ports/risc-v32/gnu/src/tx_thread_system_return.S57
-rw-r--r--ports/risc-v32/gnu/src/tx_timer_interrupt.S4
17 files changed, 600 insertions, 600 deletions
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/.gitignore b/ports/risc-v32/gnu/example_build/qemu_virt/.gitignore
new file mode 100644
index 00000000..18a4075c
--- /dev/null
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/.gitignore
@@ -0,0 +1,4 @@
+# Ignore generated QEMU test files.
+kernel.elf
+qemu-riscv32.log
+test_cmds.gdb
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt b/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt
index 43e757ac..cbf67798 100644
--- a/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt
@@ -11,7 +11,7 @@ add_executable(kernel.elf EXCLUDE_FROM_ALL
${QEMU_DEMO_DIR}/tx_initialize_low_level.S
)
-target_link_libraries(kernel.elf PRIVATE threadx)
+target_link_libraries(kernel.elf PRIVATE threadx gcc)
target_include_directories(kernel.elf PRIVATE
${CMAKE_SOURCE_DIR}/common/inc
@@ -28,18 +28,32 @@ target_link_options(kernel.elf PRIVATE
# QEMU/GDB functional test runner. Optional: skipped silently if the
# host has no Python 3 interpreter on PATH.
find_package(Python3 COMPONENTS Interpreter)
-if(Python3_FOUND)
+# The runner needs a GDB that understands riscv:rv32 remote targets
+find_program(RISCV32_GDB NAMES riscv32-unknown-elf-gdb gdb-multiarch)
+find_program(RISCV32_QEMU NAMES qemu-system-riscv32)
+if(Python3_FOUND AND RISCV32_GDB AND RISCV32_QEMU)
+ set(RISCV32_QEMU_TEST_ARGS)
+ if(SOFT_FLOAT)
+ list(APPEND RISCV32_QEMU_TEST_ARGS --skip-fpu)
+ endif()
add_custom_target(check-functional-riscv32
COMMAND ${Python3_EXECUTABLE}
${QEMU_DEMO_DIR}/test/threadx_test_tx_gnu_riscv32_qemu.py
--elf $<TARGET_FILE:kernel.elf>
- --qemu qemu-system-riscv32
- --gdb gdb
+ --qemu ${RISCV32_QEMU}
+ --gdb ${RISCV32_GDB}
+ ${RISCV32_QEMU_TEST_ARGS}
DEPENDS kernel.elf
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running RISC-V32 QEMU/GDB functional test runner..."
)
-else()
+elseif(NOT Python3_FOUND)
message(STATUS
"Python3 not found; check-functional-riscv32 target unavailable.")
+elseif(NOT RISCV32_GDB)
+ message(STATUS
+ "RISC-V GDB not found; check-functional-riscv32 target unavailable.")
+else()
+ message(STATUS
+ "qemu-system-riscv32 not found; check-functional-riscv32 target unavailable.")
endif()
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/board.c b/ports/risc-v32/gnu/example_build/qemu_virt/board.c
index fc75de66..4d0056a8 100644
--- a/ports/risc-v32/gnu/example_build/qemu_virt/board.c
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/board.c
@@ -14,15 +14,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;
}
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c b/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c
index 8288e749..c88ac9e2 100644
--- a/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c
@@ -20,7 +20,9 @@
#define DEMO_BYTE_POOL_SIZE 9120
#define DEMO_BLOCK_POOL_SIZE 100
#define DEMO_QUEUE_SIZE 100
+#ifdef __riscv_flen
float fpu_test_val = 0.0f;
+#endif
char *_to_str(ULONG val)
{
@@ -375,8 +377,10 @@ UINT status;
if (status != TX_SUCCESS)
break;
+#ifdef __riscv_flen
/* FPU Test */
fpu_test_val += 1.1f;
+#endif
/* Get the mutex again with suspension. This shows
that an owning thread may retrieve the mutex it
owns multiple times. */
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.c b/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.c
index 33c49b63..8d2f4e15 100644
--- a/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.c
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.c
@@ -8,7 +8,7 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
-#include "tx_port.h"
+#include "tx_api.h"
#include "csr.h"
#include "hwtimer.h"
@@ -16,20 +16,69 @@
#define CLINT_TIME (CLINT+0xBFF8)
#define CLINT_TIMECMP(hart_id) (CLINT+0x4000+8*(hart_id))
+/* RV32 has no 64-bit load/store, so the 64-bit CLINT registers are
+ accessed as two volatile 32-bit MMIO words. */
+#define MTIME_LO (*(volatile uint32_t *)(CLINT_TIME))
+#define MTIME_HI (*(volatile uint32_t *)(CLINT_TIME + 4))
+#define MTIMECMP_LO(hart) (*(volatile uint32_t *)(CLINT_TIMECMP(hart)))
+#define MTIMECMP_HI(hart) (*(volatile uint32_t *)(CLINT_TIMECMP(hart) + 4))
+
+/* RV32 mtime re-read the high word until it is stable, so a
+ low-word rollover between the two loads cannot tear the value. */
+static uint64_t clint_time_read(void)
+{
+ uint32_t hi;
+ uint32_t lo;
+
+ do
+ {
+ hi = MTIME_HI;
+ lo = MTIME_LO;
+ } while (hi != MTIME_HI);
+
+ return ((uint64_t)hi << 32) | lo;
+}
+
+/* Only this hart writes its own mtimecmp, so a plain two-word read
+ cannot tear. */
+static uint64_t clint_timecmp_read(int hart)
+{
+ uint32_t lo = MTIMECMP_LO(hart);
+ uint32_t hi = MTIMECMP_HI(hart);
+
+ return ((uint64_t)hi << 32) | lo;
+}
+
+/* RV32 mtimecmp write, safe sequence: park the low word at all-ones
+ first, so no intermediate 64-bit value compares below mtime and
+ raises a spurious timer interrupt. */
+static void clint_timecmp_write(int hart, uint64_t value)
+{
+ MTIMECMP_LO(hart) = 0xFFFFFFFF;
+ MTIMECMP_HI(hart) = (uint32_t)(value >> 32);
+ MTIMECMP_LO(hart) = (uint32_t)value;
+}
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();
+
+ clint_timecmp_write(hart, clint_time_read() + 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 = clint_timecmp_read(hart) + TICKNUM_PER_TIMER;
+ uint64_t now = clint_time_read();
+ if (next <= now)
+ next = now + TICKNUM_PER_TIMER;
+
+ clint_timecmp_write(hart, next);
+ return 0;
+}
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.h b/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.h
index 966b1abf..7bc2d0e3 100644
--- a/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.h
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.h
@@ -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);
int hwtimer_handler(void);
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/link.lds b/ports/risc-v32/gnu/example_build/qemu_virt/link.lds
index d2ef5f28..42f5d366 100644
--- a/ports/risc-v32/gnu/example_build/qemu_virt/link.lds
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/link.lds
@@ -1,6 +1,12 @@
OUTPUT_ARCH( "riscv" )
ENTRY( _start )
+PHDRS
+{
+ text PT_LOAD FLAGS(5);
+ data PT_LOAD FLAGS(6);
+}
+
SECTIONS
{
/*
@@ -14,15 +20,16 @@ SECTIONS
*(.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);
/* Centre __global_pointer$ in the small-data window so the +/-2 KiB
@@ -31,23 +38,23 @@ SECTIONS
*(.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;
_sysstack_end = .;
- }
+ } :data
PROVIDE(_end = .);
}
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py b/ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py
index 6123cd73..bc97e745 100644
--- a/ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py
@@ -17,134 +17,186 @@ import socket
import select
def print_content(content):
- """Prints content using os.write to handle non-blocking stdout robustly."""
- try:
- msg = f"{content}\n".encode('utf-8')
- total_len = len(msg)
- written = 0
- fd = sys.stdout.fileno()
- while written < total_len:
- try:
- n = os.write(fd, msg[written:])
- written += n
- except BlockingIOError:
- select.select([], [fd], [])
- except Exception:
- pass
+ """Write content to nonblocking stdout with os.write."""
+ msg = f"{content}\n".encode('utf-8')
+ total_len = len(msg)
+ written = 0
+ fd = sys.stdout.fileno()
+ while written < total_len:
+ try:
+ n = os.write(fd, msg[written:])
+ written += n
+ except BlockingIOError:
+ select.select([], [fd], [])
def get_free_port():
- """Finds a free TCP port."""
+ """Get an unused TCP port."""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('', 0))
return s.getsockname()[1]
-def run_qemu_test(elf_path, qemu_bin, gdb_bin):
- """
- Runs a test cycle using QEMU and GDB.
- """
+def run_qemu_test(elf_path, qemu_bin, gdb_bin, skip_fpu):
+ """Run one QEMU and GDB test cycle."""
print(f"Testing ELF: {elf_path}")
print(f"QEMU: {qemu_bin}")
print(f"GDB: {gdb_bin}")
- # Find a free port for GDB connection
+ # Get an unused GDB port.
gdb_port = get_free_port()
print(f"Using GDB port: {gdb_port}")
- # 1. Start QEMU in the background
+ # Start QEMU and wait for GDB.
+ GDB_TIMEOUT_S = 120
+ qemu_log_path = os.path.abspath("qemu-riscv32.log")
qemu_cmd = [
qemu_bin,
"-M", "virt",
"-nographic",
- "-bios", "none", # Disable default OpenSBI to avoid overlap at 0x80000000
+ "-bios", "none", # Start without OpenSBI at 0x80000000.
"-kernel", elf_path,
"-gdb", f"tcp::{gdb_port}", "-S",
- "-monitor", "none", # Disable monitor to avoid clutter
- "-serial", "stdio" # Redirect serial output to stdio so we can see it
+ "-monitor", "none", # Disable the QEMU monitor.
+ "-serial", "stdio" # Send serial output to stdout.
]
print(f"Starting QEMU: {' '.join(qemu_cmd)}")
- qemu_process = subprocess.Popen(
- qemu_cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True
+ qemu_log = open(qemu_log_path, "w", encoding="utf-8")
+ try:
+ qemu_process = subprocess.Popen(
+ qemu_cmd,
+ stdout=qemu_log,
+ stderr=subprocess.STDOUT,
+ text=True
+ )
+ except OSError as e:
+ qemu_log.close()
+ print_content(f"FAILURE: QEMU did not start: {e}")
+ return False
+
+ print_content(
+ f"QEMU PID {qemu_process.pid}; expected runtime under 30s; "
+ f"wall timeout {GDB_TIMEOUT_S}s; log {qemu_log_path}"
)
if qemu_process.poll() is not None:
- print("QEMU failed to start.")
- print(qemu_process.stderr.read())
+ qemu_log.close()
+ print_content("FAILURE: QEMU exited during startup.")
+ with open(qemu_log_path, encoding="utf-8") as failed_log:
+ print_content(failed_log.read())
return False
- # 2. Create a GDB command file
- # We use a defined command for the timer interrupt to perform the check automatically
+ # Create the GDB command file.
gdb_cmds = """
file {elf}
target remote :{port}
set pagination off
set confirm off
-# Setup Breakpoints
+# Set breakpoints.
break tx_application_define
break thread_0_entry
break thread_6_and_7_entry
break _tx_timer_interrupt
-# Execute to Application Definition
+disable 4
+
+# Run to the application setup.
continue
-# Inspect mstatus once thread_0 has started (FS bits should be observable;
-# kept as a smoke check, the lazy-save logic itself is targeted by a
-# follow-up PR).
+
continue
print/x $mstatus
-
-# Verify FPU Logic and Register State exercised by thread_6/7
continue
-finish
-step
-step
-step
+
+# Check for fpu_test_val before GDB uses it.
+# An absent symbol fails the required FPU test.
+python gdb.execute("set $fpu_sym = %d" % (1 if gdb.lookup_global_symbol("fpu_test_val") else 0))
+set $fpu_required = {fpu_required}
+
+set $fpu_iter = 0
+if $fpu_sym != 0
+ set $fpu_before = fpu_test_val
+ # Continue until one FPU addition completes.
+ # Limit the wait to 64 thread passes.
+ while fpu_test_val == $fpu_before && $fpu_iter < 64
+ set $fpu_iter = $fpu_iter + 1
+ continue
+ end
+end
+
print/x $mstatus
info registers float
-print fpu_test_val
-# Await Timer Interrupt
+if $fpu_sym == 0
+ if $fpu_required != 0
+ printf "FPU_VERIFIED_FAIL_NO_SYMBOL\\n"
+ else
+ printf "FPU_VERIFIED_SKIP_SOFT_FLOAT\\n"
+ end
+else
+ print fpu_test_val
+ # Check for a multiple of 1.1f with a 0.01 tolerance.
+ 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
+
+# Enable only the timer breakpoint.
+# Then $ra contains the ISR return address.
+disable 1
+disable 2
+disable 3
+enable 4
continue
print "Hit Timer Interrupt"
-# Verify MEPC Integrity - Save State
+# Save mepc at timer entry.
print/x $mepc
set $saved_pc = $mepc
-# Verify System Timer Before ISR
+# Read the system clock before the ISR completes.
set $clock_before = _tx_timer_system_clock
print $clock_before
-# Configure Time-Slice Test Conditions
+# Force time-slice processing.
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
+# Record time-slice handler calls.
tbreak _tx_thread_time_slice
commands
set $ts_handler_called = 1
continue
end
-# Set Breakpoint at ISR Return Address
+# Stop at the ISR return address.
+# Disable the timer breakpoint during this interval.
set $ret_addr = $ra
+disable 4
tbreak *$ret_addr
continue
-# Verify Time-Slice Handler Was Called
+# Check the live CSR and the saved frame PC.
+set $frame_pc = ((unsigned int *)_tx_thread_current_ptr->tx_thread_stack_ptr)[30]
+if $mepc == $saved_pc && $frame_pc == $saved_pc
+ printf "MEPC_VERIFIED_OK mepc=0x%x frame_pc=0x%x\\n", $mepc, $frame_pc
+else
+ printf "MEPC_VERIFIED_FAIL saved=0x%x mepc=0x%x frame_pc=0x%x\\n", $saved_pc, $mepc, $frame_pc
+end
+
+# Check time-slice handler execution.
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)
+# Check that the system clock increased.
set $clock_after = _tx_timer_system_clock
print $clock_after
@@ -154,37 +206,56 @@ 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 has had a chance to update
-# _tx_thread_execute_ptr but before trap_handler returns into
-# _tx_thread_context_restore. At this point, a pending preemption is
-# observable directly by comparing current_ptr (interrupted thread)
-# and execute_ptr (thread chosen by the scheduler).
-set $curr_ptr = _tx_thread_current_ptr
-set $exec_ptr = _tx_thread_execute_ptr
-if $curr_ptr != 0 && $exec_ptr != 0
- set $curr_prio = $curr_ptr->tx_thread_priority
- set $exec_prio = $exec_ptr->tx_thread_priority
- printf "PREEMPT_CHECK current_prio=%d execute_prio=%d\\n", $curr_prio, $exec_prio
- if $exec_prio < $curr_prio
- printf "PREEMPT_VERIFIED_OK\\n"
+# Compare the current and selected thread priorities at the ISR return.
+# Check each tick until preemption occurs or 250 ticks expire.
+# Each retry uses the same ISR point and starts a new time slice.
+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
- printf "PREEMPT_VERIFIED_FAIL_NOT_OBSERVED\\n"
+ 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
- printf "PREEMPT_VERIFIED_FAIL_NULL\\n"
+ 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
-""".format(port=gdb_port, elf=elf_path)
+""".format(port=gdb_port, elf=elf_path, fpu_required=0 if skip_fpu else 1)
gdb_cmd_file = "test_cmds.gdb"
with open(gdb_cmd_file, "w") as f:
f.write(gdb_cmds)
- # 3. Run GDB
+ # Run GDB in batch mode.
gdb_cmd = [
gdb_bin,
"--batch",
@@ -193,10 +264,8 @@ quit
print_content(f"Starting GDB: {' '.join(gdb_cmd)}")
- # Cap the GDB session to 30 s so a wedged batch script (e.g. a
- # `continue` that never hits its breakpoint) cannot hang CI.
- GDB_TIMEOUT_S = 30
-
+ # Limit GDB to 120 seconds.
+ # The loop uses at most 250 ticks and two gdbstub exchanges for each tick.
try:
gdb_process = subprocess.run(
gdb_cmd,
@@ -232,25 +301,46 @@ quit
return False
finally:
- # 4. Clean up
+ # Stop QEMU and close its log.
print_content("Stopping QEMU...")
qemu_process.terminate()
try:
qemu_process.wait(timeout=2)
except subprocess.TimeoutExpired:
- print_content("QEMU did not terminate gracefully, killing it forcefullly.")
+ print_content("QEMU did not terminate gracefully, killing it forcefully.")
qemu_process.kill()
+ qemu_process.wait()
+ qemu_log.close()
- # Verify results
+ # Check each required test result.
stdout = gdb_process.stdout
+ gdb_succeeded = gdb_process.returncode == 0
timer_hit = "Breakpoint 4, _tx_timer_interrupt" in stdout
fpu_verified = False
+ mepc_verified = "MEPC_VERIFIED_OK" in stdout
preemption_verified = "PREEMPT_VERIFIED_OK" in stdout
+ time_slice_verified = "SUCCESS: Time-slice handler called." in stdout
+ system_timer_verified = "SUCCESS: System timer incremented." in stdout
+
+ if not gdb_succeeded:
+ print_content(f"FAILURE: GDB exited with status {gdb_process.returncode}.")
- if "Breakpoint 3, thread_6_and_7_entry" in stdout:
- if "1.10" in stdout or "fpu_test_val" in stdout:
+ if skip_fpu and "FPU_VERIFIED_SKIP_SOFT_FLOAT" in stdout:
+ print_content("SUCCESS: Soft-float mode omitted the hardware FPU check.")
+ fpu_verified = True
+ elif skip_fpu:
+ print_content("FAILURE: Soft-float build did not report the FPU skip, so "
+ "the GDB script did not run to that point.")
+ elif "Breakpoint 3, thread_6_and_7_entry" in stdout:
+ if "FPU_VERIFIED_OK" in stdout:
print_content("SUCCESS: FPU instructions executed and registers inspected.")
fpu_verified = True
+ elif "FPU_VERIFIED_FAIL_NO_SYMBOL" in stdout:
+ print_content("FAILURE: Hit thread, but this demo defines no "
+ "fpu_test_val, so the FPU result is unverifiable.")
+ elif "FPU_VERIFIED_FAIL" in stdout:
+ print_content("FAILURE: Hit thread, but fpu_test_val is not an exact "
+ "multiple of 1.1f. Output does not contain expected value.")
else:
print_content("FAILURE: Hit thread, but failed to inspect FPU. Output does not contain expected value.")
@@ -259,6 +349,21 @@ quit
else:
print_content("FAILURE: Did not hit timer interrupt.")
+ if mepc_verified:
+ print_content("SUCCESS: MEPC and the saved frame PC are unchanged.")
+ else:
+ print_content("FAILURE: MEPC or the saved frame PC changed in the ISR.")
+
+ if time_slice_verified:
+ print_content("SUCCESS: Time-slice handler verified.")
+ else:
+ print_content("FAILURE: Time-slice handler was not called.")
+
+ if system_timer_verified:
+ print_content("SUCCESS: System timer increment verified.")
+ else:
+ print_content("FAILURE: System timer did not increment.")
+
if preemption_verified:
print_content("SUCCESS: Preemption verified (higher-priority thread "
"preempted a lower-priority one).")
@@ -274,7 +379,9 @@ quit
else:
print_content("FAILURE: Preemption check did not run to completion.")
- if timer_hit and fpu_verified and preemption_verified:
+ if (gdb_succeeded and timer_hit and fpu_verified and mepc_verified and
+ time_slice_verified and system_timer_verified and
+ preemption_verified):
return True
else:
return False
@@ -284,10 +391,12 @@ if __name__ == "__main__":
parser.add_argument("--elf", required=True, help="Path to the kernel ELF file")
parser.add_argument("--qemu", default="qemu-system-riscv32", help="Path to QEMU binary")
parser.add_argument("--gdb", default="riscv-none-elf-gdb", help="Path to GDB binary")
+ parser.add_argument("--skip-fpu", action="store_true",
+ help="Skip the hardware FPU check for a soft-float build")
args = parser.parse_args()
- success = run_qemu_test(args.elf, args.qemu, args.gdb)
+ success = run_qemu_test(args.elf, args.qemu, args.gdb, args.skip_fpu)
if success:
sys.exit(0)
diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S b/ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S
index 9a7a74ff..9cda8096 100644
--- a/ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S
+++ b/ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S
@@ -59,12 +59,12 @@
.global trap_entry
.extern trap_handler
.extern _tx_thread_context_restore
+/* Trap frame: integer slots 0-31 at i*4; FP build adds f0-f31 as doubles
+ at 128 + i*8, fcsr at 384, pad to 400 (total mod 16 = 0). */
+#include "tx_port.h"
+ .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
trap_entry:
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- addi sp, sp, -260 // Allocate space for all registers - with floating point enabled (65*4)
-#else
- addi sp, sp, -128 // Allocate space for all registers - without floating point enabled (32*4)
-#endif
+ addi sp, sp, -TX_TRAP_FRAME_SIZE // Allocate space for all registers (400 with FP double, 128 without)
sw x1, 112(sp) // Store RA (28*4 = 112, because call will override ra [ra is a callee register in riscv])
@@ -73,11 +73,11 @@
csrr a0, mcause
csrr a1, mepc
csrr a2, mtval
- addi sp, sp, -4
+ addi sp, sp, -16 // 16-byte temporary frame: ilp32 psABI requires sp = 0 mod 16 at the call
sw ra, 0(sp)
call trap_handler
lw ra, 0(sp)
- addi sp, sp, 4
+ addi sp, sp, 16
call _tx_thread_context_restore
// it will nerver return
_err:
@@ -128,7 +128,6 @@ _err:
/* VOID _tx_initialize_low_level(VOID)
{ */
.global _tx_initialize_low_level
- .weak _tx_initialize_low_level
.extern _end
.extern board_init
_tx_initialize_low_level:
@@ -147,16 +146,16 @@ _tx_initialize_low_level:
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
+#if 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
#endif
- addi sp, sp, -4
+ addi sp, sp, -16 // 16-byte temporary frame: ilp32 psABI requires sp = 0 mod 16 at the call
sw ra, 0(sp)
call board_init
lw ra, 0(sp)
- addi sp, sp, 4
+ addi sp, sp, 16
la t0, trap_entry
csrw mtvec, t0
ret
diff --git a/ports/risc-v32/gnu/inc/tx_port.h b/ports/risc-v32/gnu/inc/tx_port.h
index c990aa97..3ca02ec9 100644
--- a/ports/risc-v32/gnu/inc/tx_port.h
+++ b/ports/risc-v32/gnu/inc/tx_port.h
@@ -25,7 +25,7 @@
/* PORT SPECIFIC C INFORMATION RELEASE */
/* */
/* tx_port.h RISC-V32/GNU */
-/* 6.4.x */
+/* 6.5.1.202602a */
/* */
/* AUTHOR */
/* */
@@ -47,6 +47,46 @@
#ifndef TX_PORT_H
#define TX_PORT_H
+#ifdef __riscv_vector
+#error "The ThreadX RISC-V32 GNU port does not save or restore vector state. Build without the V extension."
+#endif
+
+#ifdef __riscv_32e
+#error "The ThreadX RISC-V32 GNU port requires the RV32I register set. RV32E is not supported."
+#endif
+
+#ifdef __riscv_float_abi_single
+#error "The ThreadX RISC-V32 GNU port does not support ILP32F. Use ILP32D or a soft-float ABI."
+#endif
+
+#ifdef __riscv_float_abi_quad
+#error "The ThreadX RISC-V32 GNU port does not support ILP32Q."
+#endif
+
+#if defined(__riscv_flen) && !defined(__riscv_float_abi_double)
+#error "The ThreadX RISC-V32 GNU port requires ILP32D when the ISA enables floating-point state."
+#endif
+
+#if defined(__riscv_float_abi_double) && (!defined(__riscv_flen) || (__riscv_flen != 64))
+#error "The ThreadX RISC-V32 GNU ILP32D port requires FLEN=64."
+#endif
+
+/* Publish the interrupt frame contract to GNU BSP assembly files. */
+#if defined(__riscv_float_abi_double)
+#define TX_RISCV_TRAP_FRAME_SIZE 400
+#else
+#define TX_RISCV_TRAP_FRAME_SIZE 128
+#endif
+#define TX_RISCV_TRAP_CALL_FRAME_SIZE 16
+
+/* Solicited (cooperative) frame built by _tx_thread_system_return: fs0-fs11
+ as doubles plus fcsr with FP, callee-saved integer state without. */
+#if defined(__riscv_float_abi_double)
+#define TX_RISCV_SOL_FRAME_SIZE 176
+#else
+#define TX_RISCV_SOL_FRAME_SIZE 128
+#endif
+
/* Include shared RISC-V32 port definitions common to all toolchain ports. */
#include "../../common/tx_port_riscv32_common.h"
@@ -157,7 +197,7 @@ extern TEST_FLAG test_forced_mutex_timeout;
#ifndef __ASSEMBLER__
#ifdef TX_THREAD_INIT
CHAR _tx_version_id[] =
- "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX RISC-V32/GNU Version 6.5.1.202602a *";
+ "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX RISC-V32/GNU Version 6.5.2.202603 *";
#else
extern CHAR _tx_version_id[];
#endif /* TX_THREAD_INIT */
diff --git a/ports/risc-v32/gnu/src/tx_initialize_low_level.S b/ports/risc-v32/gnu/src/tx_initialize_low_level.S
index 2940f215..ca97ba62 100644
--- a/ports/risc-v32/gnu/src/tx_initialize_low_level.S
+++ b/ports/risc-v32/gnu/src/tx_initialize_low_level.S
@@ -62,8 +62,7 @@ __tx_free_memory_start:
/**************************************************************************/
/* VOID _tx_initialize_low_level(VOID)
{ */
- .weak _tx_initialize_low_level
- .weak _tx_initialize_low_level
+ .weak _tx_initialize_low_level // Weak: a BSP definition overrides this default
_tx_initialize_low_level:
/* Save the system stack pointer. */
@@ -79,8 +78,10 @@ _tx_initialize_low_level:
la t1, _tx_initialize_unused_memory // Pickup address of unused memory
sw t0, 0(t1) // Save unused memory address
- /* Initialize floating point control/status register if floating point is enabled. */
-#ifdef __riscv_flen
+ /* Initialize floating point state if floating point is enabled. */
+#if defined(__riscv_float_abi_double)
+ li t0, 0x2000 // mstatus.FS = Initial (bits 14:13 = 01)
+ csrrs zero, mstatus, t0 // Enable the FP unit before touching fcsr
li t0, 0
csrw fcsr, t0 // Clear FP control/status register
#endif
diff --git a/ports/risc-v32/gnu/src/tx_thread_context_restore.S b/ports/risc-v32/gnu/src/tx_thread_context_restore.S
index d3d449e4..c4854643 100644
--- a/ports/risc-v32/gnu/src/tx_thread_context_restore.S
+++ b/ports/risc-v32/gnu/src/tx_thread_context_restore.S
@@ -20,6 +20,14 @@
/**************************************************************************/
.section .text
+
+/* Trap frame layout (stack grows down, all totals mod 16 = 0):
+ FP build (double ABI): integer slots 0-31 at i*4 (0-127; slot 31 is pad),
+ f0-f31 as doubles at 128 + i*8 (128-383), fcsr at 384, pad 388-399.
+ SOFT_FLOAT build: integer slots 0-31 at i*4 only. */
+#include "tx_port.h"
+ .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
+
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
@@ -84,57 +92,32 @@ _tx_thread_context_restore:
interrupt. */
/* Recover floating point registers only if saved mstatus.FS was not Off. */
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
lw t1, 29*4(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
-#if defined(__riscv_float_abi_single)
- flw f0, 31*4(sp) // Recover ft0
- flw f1, 32*4(sp) // Recover ft1
- flw f2, 33*4(sp) // Recover ft2
- flw f3, 34*4(sp) // Recover ft3
- flw f4, 35*4(sp) // Recover ft4
- flw f5, 36*4(sp) // Recover ft5
- flw f6, 37*4(sp) // Recover ft6
- flw f7, 38*4(sp) // Recover ft7
- flw f10, 41*4(sp) // Recover fa0
- flw f11, 42*4(sp) // Recover fa1
- flw f12, 43*4(sp) // Recover fa2
- flw f13, 44*4(sp) // Recover fa3
- flw f14, 45*4(sp) // Recover fa4
- flw f15, 46*4(sp) // Recover fa5
- flw f16, 47*4(sp) // Recover fa6
- flw f17, 48*4(sp) // Recover fa7
- flw f28, 59*4(sp) // Recover ft8
- flw f29, 60*4(sp) // Recover ft9
- flw f30, 61*4(sp) // Recover ft10
- flw f31, 62*4(sp) // Recover ft11
- lw t0, 63*4(sp) // Recover fcsr
- csrw fcsr, t0 // Restore fcsr
-#elif defined(__riscv_float_abi_double)
- fld f0, 31*4(sp) // Recover ft0
- fld f1, 32*4(sp) // Recover ft1
- fld f2, 33*4(sp) // Recover ft2
- fld f3, 34*4(sp) // Recover ft3
- fld f4, 35*4(sp) // Recover ft4
- fld f5, 36*4(sp) // Recover ft5
- fld f6, 37*4(sp) // Recover ft6
- fld f7, 38*4(sp) // Recover ft7
- fld f10, 41*4(sp) // Recover fa0
- fld f11, 42*4(sp) // Recover fa1
- fld f12, 43*4(sp) // Recover fa2
- fld f13, 44*4(sp) // Recover fa3
- fld f14, 45*4(sp) // Recover fa4
- fld f15, 46*4(sp) // Recover fa5
- fld f16, 47*4(sp) // Recover fa6
- fld f17, 48*4(sp) // Recover fa7
- fld f28, 59*4(sp) // Recover ft8
- fld f29, 60*4(sp) // Recover ft9
- fld f30, 61*4(sp) // Recover ft10
- fld f31, 62*4(sp) // Recover ft11
- lw t0, 63*4(sp) // Recover fcsr
+ fld f0, 16*8(sp) // Recover ft0
+ fld f1, 17*8(sp) // Recover ft1
+ fld f2, 18*8(sp) // Recover ft2
+ fld f3, 19*8(sp) // Recover ft3
+ fld f4, 20*8(sp) // Recover ft4
+ fld f5, 21*8(sp) // Recover ft5
+ fld f6, 22*8(sp) // Recover ft6
+ fld f7, 23*8(sp) // Recover ft7
+ fld f10, 26*8(sp) // Recover fa0
+ fld f11, 27*8(sp) // Recover fa1
+ fld f12, 28*8(sp) // Recover fa2
+ fld f13, 29*8(sp) // Recover fa3
+ fld f14, 30*8(sp) // Recover fa4
+ fld f15, 31*8(sp) // Recover fa5
+ fld f16, 32*8(sp) // Recover fa6
+ fld f17, 33*8(sp) // Recover fa7
+ fld f28, 44*8(sp) // Recover ft8
+ fld f29, 45*8(sp) // Recover ft9
+ fld f30, 46*8(sp) // Recover ft10
+ fld f31, 47*8(sp) // Recover ft11
+ lw t0, 96*4(sp) // Recover fcsr
csrw fcsr, t0 // Restore fcsr
#endif
_tx_thread_skip_fp_restore:
@@ -166,7 +149,7 @@ _tx_thread_skip_fp_restore:
and t1, t1, t4
or t1, t1, t3
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
li t0, 0x2000 // Set FS bits (bits 14:13 to 01) for FP state
or t1, t1, t0
#endif
@@ -190,11 +173,7 @@ _tx_thread_skip_fp_restore:
lw t5, 14*4(sp) // Recover t5
lw t6, 13*4(sp) // Recover t6
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- addi sp, sp, 65*4 // Recover stack frame - with floating point enabled
-#else
- addi sp, sp, 32*4 // Recover stack frame - without floating point enabled
-#endif
+ addi sp, sp, TX_TRAP_FRAME_SIZE // Recover stack frame
mret // Return to point of interrupt
/* } */
@@ -213,7 +192,7 @@ _tx_thread_not_nested_restore:
la t0, _tx_thread_preempt_disable // Pickup preempt disable flag address
lw t2, 0(t0) // Pickup preempt disable flag (UINT)
- bgtz t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread
+ bnez t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread
la t0, _tx_thread_execute_ptr // Pickup thread execute pointer address
@@ -231,57 +210,32 @@ _tx_thread_no_preempt_restore:
lw sp, 8(t1) // Switch back to thread's stack
/* Recover floating point registers only if saved mstatus.FS was not Off. */
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
lw t3, 29*4(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
-#if defined(__riscv_float_abi_single)
- flw f0, 31*4(sp) // Recover ft0
- flw f1, 32*4(sp) // Recover ft1
- flw f2, 33*4(sp) // Recover ft2
- flw f3, 34*4(sp) // Recover ft3
- flw f4, 35*4(sp) // Recover ft4
- flw f5, 36*4(sp) // Recover ft5
- flw f6, 37*4(sp) // Recover ft6
- flw f7, 38*4(sp) // Recover ft7
- flw f10, 41*4(sp) // Recover fa0
- flw f11, 42*4(sp) // Recover fa1
- flw f12, 43*4(sp) // Recover fa2
- flw f13, 44*4(sp) // Recover fa3
- flw f14, 45*4(sp) // Recover fa4
- flw f15, 46*4(sp) // Recover fa5
- flw f16, 47*4(sp) // Recover fa6
- flw f17, 48*4(sp) // Recover fa7
- flw f28, 59*4(sp) // Recover ft8
- flw f29, 60*4(sp) // Recover ft9
- flw f30, 61*4(sp) // Recover ft10
- flw f31, 62*4(sp) // Recover ft11
- lw t0, 63*4(sp) // Recover fcsr
- csrw fcsr, t0 // Restore fcsr
-#elif defined(__riscv_float_abi_double)
- fld f0, 31*4(sp) // Recover ft0
- fld f1, 32*4(sp) // Recover ft1
- fld f2, 33*4(sp) // Recover ft2
- fld f3, 34*4(sp) // Recover ft3
- fld f4, 35*4(sp) // Recover ft4
- fld f5, 36*4(sp) // Recover ft5
- fld f6, 37*4(sp) // Recover ft6
- fld f7, 38*4(sp) // Recover ft7
- fld f10, 41*4(sp) // Recover fa0
- fld f11, 42*4(sp) // Recover fa1
- fld f12, 43*4(sp) // Recover fa2
- fld f13, 44*4(sp) // Recover fa3
- fld f14, 45*4(sp) // Recover fa4
- fld f15, 46*4(sp) // Recover fa5
- fld f16, 47*4(sp) // Recover fa6
- fld f17, 48*4(sp) // Recover fa7
- fld f28, 59*4(sp) // Recover ft8
- fld f29, 60*4(sp) // Recover ft9
- fld f30, 61*4(sp) // Recover ft10
- fld f31, 62*4(sp) // Recover ft11
- lw t0, 63*4(sp) // Recover fcsr
+ fld f0, 16*8(sp) // Recover ft0
+ fld f1, 17*8(sp) // Recover ft1
+ fld f2, 18*8(sp) // Recover ft2
+ fld f3, 19*8(sp) // Recover ft3
+ fld f4, 20*8(sp) // Recover ft4
+ fld f5, 21*8(sp) // Recover ft5
+ fld f6, 22*8(sp) // Recover ft6
+ fld f7, 23*8(sp) // Recover ft7
+ fld f10, 26*8(sp) // Recover fa0
+ fld f11, 27*8(sp) // Recover fa1
+ fld f12, 28*8(sp) // Recover fa2
+ fld f13, 29*8(sp) // Recover fa3
+ fld f14, 30*8(sp) // Recover fa4
+ fld f15, 31*8(sp) // Recover fa5
+ fld f16, 32*8(sp) // Recover fa6
+ fld f17, 33*8(sp) // Recover fa7
+ fld f28, 44*8(sp) // Recover ft8
+ fld f29, 45*8(sp) // Recover ft9
+ fld f30, 46*8(sp) // Recover ft10
+ fld f31, 47*8(sp) // Recover ft11
+ lw t0, 96*4(sp) // Recover fcsr
csrw fcsr, t0 // Restore fcsr
#endif
_tx_thread_no_preempt_skip_fp_restore:
@@ -304,7 +258,7 @@ _tx_thread_no_preempt_skip_fp_restore:
and t1, t1, t4
or t1, t1, t3
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
li t0, 0x2000 // Set FS bits for FP state
or t1, t1, t0
#endif
@@ -328,11 +282,7 @@ _tx_thread_no_preempt_skip_fp_restore:
lw t5, 14*4(sp) // Recover t5
lw t6, 13*4(sp) // Recover t6
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- addi sp, sp, 65*4 // Recover stack frame - with floating point enabled
-#else
- addi sp, sp, 32*4 // Recover stack frame - without floating point enabled
-#endif
+ addi sp, sp, TX_TRAP_FRAME_SIZE // Recover stack frame
mret // Return to point of interrupt
/* }
@@ -347,38 +297,23 @@ _tx_thread_preempt_restore:
sw t3, 0(t0) // Store stack type
/* Store floating point preserved registers only if saved mstatus.FS was not Off. */
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
lw t3, 29*4(t0) // Pickup saved mstatus
srli t3, t3, 13
andi t3, t3, 0x3
beqz t3, _tx_thread_preempt_skip_fp_restore // Skip if FS was Off
-#endif
-#ifdef __riscv_float_abi_single
- fsw f8, 39*4(t0) // Store fs0
- fsw f9, 40*4(t0) // Store fs1
- fsw f18, 49*4(t0) // Store fs2
- fsw f19, 50*4(t0) // Store fs3
- fsw f20, 51*4(t0) // Store fs4
- fsw f21, 52*4(t0) // Store fs5
- fsw f22, 53*4(t0) // Store fs6
- fsw f23, 54*4(t0) // Store fs7
- fsw f24, 55*4(t0) // Store fs8
- fsw f25, 56*4(t0) // Store fs9
- fsw f26, 57*4(t0) // Store fs10
- fsw f27, 58*4(t0) // Store fs11
-#elif defined(__riscv_float_abi_double)
- fsd f8, 39*4(t0) // Store fs0
- fsd f9, 40*4(t0) // Store fs1
- fsd f18, 49*4(t0) // Store fs2
- fsd f19, 50*4(t0) // Store fs3
- fsd f20, 51*4(t0) // Store fs4
- fsd f21, 52*4(t0) // Store fs5
- fsd f22, 53*4(t0) // Store fs6
- fsd f23, 54*4(t0) // Store fs7
- fsd f24, 55*4(t0) // Store fs8
- fsd f25, 56*4(t0) // Store fs9
- fsd f26, 57*4(t0) // Store fs10
- fsd f27, 58*4(t0) // Store fs11
+ fsd f8, 192(t0) // Store fs0
+ fsd f9, 200(t0) // Store fs1
+ fsd f18, 272(t0) // Store fs2
+ fsd f19, 280(t0) // Store fs3
+ fsd f20, 288(t0) // Store fs4
+ fsd f21, 296(t0) // Store fs5
+ fsd f22, 304(t0) // Store fs6
+ fsd f23, 312(t0) // Store fs7
+ fsd f24, 320(t0) // Store fs8
+ fsd f25, 328(t0) // Store fs9
+ fsd f26, 336(t0) // Store fs10
+ fsd f27, 344(t0) // Store fs11
#endif
_tx_thread_preempt_skip_fp_restore:
diff --git a/ports/risc-v32/gnu/src/tx_thread_context_save.S b/ports/risc-v32/gnu/src/tx_thread_context_save.S
index 0a0c0c15..e93d5870 100644
--- a/ports/risc-v32/gnu/src/tx_thread_context_save.S
+++ b/ports/risc-v32/gnu/src/tx_thread_context_save.S
@@ -1,5 +1,6 @@
/***************************************************************************
* Copyright (c) 2025 10xEngineers
+ * Copyright (c) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
@@ -8,6 +9,7 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Portions of this file were generated with AI assistance.
/**************************************************************************/
/**************************************************************************/
@@ -20,6 +22,10 @@
/**************************************************************************/
.section .text
+
+#include "tx_port.h"
+ .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
+
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
@@ -61,7 +67,7 @@ _tx_thread_context_save:
/* Upon entry to this routine, RA/x1 has been saved on the stack
and the stack has been already allocated for the entire context:
- addi sp, sp, -32*4 (or -65*4)
+ addi sp, sp, -TX_TRAP_FRAME_SIZE (400 with FP, 128 without)
sw ra, 28*4(sp)
*/
@@ -100,59 +106,34 @@ _tx_thread_context_save:
/* Save mstatus and skip FP state if FS is Off. */
csrr t0, mstatus
sw t0, 29*4(sp)
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
srli t1, t0, 13
andi t1, t1, 0x3
beqz t1, _tx_thread_skip_fpu_save
-#endif
- /* Save floating point registers. */
-#if defined(__riscv_float_abi_single)
- fsw f0, 31*4(sp) // Store ft0
- fsw f1, 32*4(sp) // Store ft1
- fsw f2, 33*4(sp) // Store ft2
- fsw f3, 34*4(sp) // Store ft3
- fsw f4, 35*4(sp) // Store ft4
- fsw f5, 36*4(sp) // Store ft5
- fsw f6, 37*4(sp) // Store ft6
- fsw f7, 38*4(sp) // Store ft7
- fsw f10, 41*4(sp) // Store fa0
- fsw f11, 42*4(sp) // Store fa1
- fsw f12, 43*4(sp) // Store fa2
- fsw f13, 44*4(sp) // Store fa3
- fsw f14, 45*4(sp) // Store fa4
- fsw f15, 46*4(sp) // Store fa5
- fsw f16, 47*4(sp) // Store fa6
- fsw f17, 48*4(sp) // Store fa7
- fsw f28, 59*4(sp) // Store ft8
- fsw f29, 60*4(sp) // Store ft9
- fsw f30, 61*4(sp) // Store ft10
- fsw f31, 62*4(sp) // Store ft11
- csrr t0, fcsr
- sw t0, 63*4(sp) // Store fcsr
-#elif defined(__riscv_float_abi_double)
- fsd f0, 31*4(sp) // Store ft0
- fsd f1, 32*4(sp) // Store ft1
- fsd f2, 33*4(sp) // Store ft2
- fsd f3, 34*4(sp) // Store ft3
- fsd f4, 35*4(sp) // Store ft4
- fsd f5, 36*4(sp) // Store ft5
- fsd f6, 37*4(sp) // Store ft6
- fsd f7, 38*4(sp) // Store ft7
- fsd f10, 41*4(sp) // Store fa0
- fsd f11, 42*4(sp) // Store fa1
- fsd f12, 43*4(sp) // Store fa2
- fsd f13, 44*4(sp) // Store fa3
- fsd f14, 45*4(sp) // Store fa4
- fsd f15, 46*4(sp) // Store fa5
- fsd f16, 47*4(sp) // Store fa6
- fsd f17, 48*4(sp) // Store fa7
- fsd f28, 59*4(sp) // Store ft8
- fsd f29, 60*4(sp) // Store ft9
- fsd f30, 61*4(sp) // Store ft10
- fsd f31, 62*4(sp) // Store ft11
+ /* Save floating point registers (doubles at 128 + i*8). */
+ fsd f0, 16*8(sp) // Store ft0
+ fsd f1, 17*8(sp) // Store ft1
+ fsd f2, 18*8(sp) // Store ft2
+ fsd f3, 19*8(sp) // Store ft3
+ fsd f4, 20*8(sp) // Store ft4
+ fsd f5, 21*8(sp) // Store ft5
+ fsd f6, 22*8(sp) // Store ft6
+ fsd f7, 23*8(sp) // Store ft7
+ fsd f10, 26*8(sp) // Store fa0
+ fsd f11, 27*8(sp) // Store fa1
+ fsd f12, 28*8(sp) // Store fa2
+ fsd f13, 29*8(sp) // Store fa3
+ fsd f14, 30*8(sp) // Store fa4
+ fsd f15, 31*8(sp) // Store fa5
+ fsd f16, 32*8(sp) // Store fa6
+ fsd f17, 33*8(sp) // Store fa7
+ fsd f28, 44*8(sp) // Store ft8
+ fsd f29, 45*8(sp) // Store ft9
+ fsd f30, 46*8(sp) // Store ft10
+ fsd f31, 47*8(sp) // Store ft11
csrr t0, fcsr
- sw t0, 63*4(sp) // Store fcsr
+ sw t0, 96*4(sp) // Store fcsr
#endif
_tx_thread_skip_fpu_save:
@@ -173,7 +154,11 @@ _tx_thread_skip_fpu_save:
/* Call the ISR execution exit function if enabled. */
#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
+ addi sp, sp, -16 // Reserve space to save the return address
+ sw ra, 0(sp) // Save the return address
call _tx_execution_isr_enter // Call the ISR execution enter function
+ lw ra, 0(sp) // Recover the return address
+ addi sp, sp, 16 // Recover the reserved stack space
#endif
ret // Return to ISR
@@ -200,59 +185,34 @@ _tx_thread_nested_save:
/* Save mstatus and skip FP state if FS is Off. */
csrr t0, mstatus
sw t0, 29*4(sp)
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
srli t1, t0, 13
andi t1, t1, 0x3
beqz t1, _tx_thread_skip_nested_fpu_save
-#endif
- /* Save floating point registers. */
-#if defined(__riscv_float_abi_single)
- fsw f0, 31*4(sp) // Store ft0
- fsw f1, 32*4(sp) // Store ft1
- fsw f2, 33*4(sp) // Store ft2
- fsw f3, 34*4(sp) // Store ft3
- fsw f4, 35*4(sp) // Store ft4
- fsw f5, 36*4(sp) // Store ft5
- fsw f6, 37*4(sp) // Store ft6
- fsw f7, 38*4(sp) // Store ft7
- fsw f10, 41*4(sp) // Store fa0
- fsw f11, 42*4(sp) // Store fa1
- fsw f12, 43*4(sp) // Store fa2
- fsw f13, 44*4(sp) // Store fa3
- fsw f14, 45*4(sp) // Store fa4
- fsw f15, 46*4(sp) // Store fa5
- fsw f16, 47*4(sp) // Store fa6
- fsw f17, 48*4(sp) // Store fa7
- fsw f28, 59*4(sp) // Store ft8
- fsw f29, 60*4(sp) // Store ft9
- fsw f30, 61*4(sp) // Store ft10
- fsw f31, 62*4(sp) // Store ft11
- csrr t0, fcsr
- sw t0, 63*4(sp) // Store fcsr
-#elif defined(__riscv_float_abi_double)
- fsd f0, 31*4(sp) // Store ft0
- fsd f1, 32*4(sp) // Store ft1
- fsd f2, 33*4(sp) // Store ft2
- fsd f3, 34*4(sp) // Store ft3
- fsd f4, 35*4(sp) // Store ft4
- fsd f5, 36*4(sp) // Store ft5
- fsd f6, 37*4(sp) // Store ft6
- fsd f7, 38*4(sp) // Store ft7
- fsd f10, 41*4(sp) // Store fa0
- fsd f11, 42*4(sp) // Store fa1
- fsd f12, 43*4(sp) // Store fa2
- fsd f13, 44*4(sp) // Store fa3
- fsd f14, 45*4(sp) // Store fa4
- fsd f15, 46*4(sp) // Store fa5
- fsd f16, 47*4(sp) // Store fa6
- fsd f17, 48*4(sp) // Store fa7
- fsd f28, 59*4(sp) // Store ft8
- fsd f29, 60*4(sp) // Store ft9
- fsd f30, 61*4(sp) // Store ft10
- fsd f31, 62*4(sp) // Store ft11
+ /* Save floating point registers (doubles at 128 + i*8). */
+ fsd f0, 16*8(sp) // Store ft0
+ fsd f1, 17*8(sp) // Store ft1
+ fsd f2, 18*8(sp) // Store ft2
+ fsd f3, 19*8(sp) // Store ft3
+ fsd f4, 20*8(sp) // Store ft4
+ fsd f5, 21*8(sp) // Store ft5
+ fsd f6, 22*8(sp) // Store ft6
+ fsd f7, 23*8(sp) // Store ft7
+ fsd f10, 26*8(sp) // Store fa0
+ fsd f11, 27*8(sp) // Store fa1
+ fsd f12, 28*8(sp) // Store fa2
+ fsd f13, 29*8(sp) // Store fa3
+ fsd f14, 30*8(sp) // Store fa4
+ fsd f15, 31*8(sp) // Store fa5
+ fsd f16, 32*8(sp) // Store fa6
+ fsd f17, 33*8(sp) // Store fa7
+ fsd f28, 44*8(sp) // Store ft8
+ fsd f29, 45*8(sp) // Store ft9
+ fsd f30, 46*8(sp) // Store ft10
+ fsd f31, 47*8(sp) // Store ft11
csrr t0, fcsr
- sw t0, 63*4(sp) // Store fcsr
+ sw t0, 96*4(sp) // Store fcsr
#endif
_tx_thread_skip_nested_fpu_save:
@@ -261,7 +221,11 @@ _tx_thread_skip_nested_fpu_save:
/* Call the ISR execution exit function if enabled. */
#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
+ addi sp, sp, -16 // Reserve space to save the return address
+ sw ra, 0(sp) // Save the return address
call _tx_execution_isr_enter // Call the ISR execution enter function
+ lw ra, 0(sp) // Recover the return address
+ addi sp, sp, 16 // Recover the reserved stack space
#endif
ret // Return to ISR
@@ -270,16 +234,16 @@ _tx_thread_idle_system_save:
#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
+ addi sp, sp, -16 // Reserve space to save the return address
+ sw ra, 0(sp) // Save the return address
call _tx_execution_isr_enter // Call the ISR execution enter function
+ lw ra, 0(sp) // Recover the return address
+ addi sp, sp, 16 // Recover the reserved stack space
#endif
/* Interrupt occurred in the scheduling loop. */
/* }
} */
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- addi sp, sp, 65*4 // Recover stack frame - with floating point enabled
-#else
- addi sp, sp, 32*4 // Recover the reserved stack space
-#endif
+ addi sp, sp, TX_TRAP_FRAME_SIZE // Recover the reserved stack space
ret // Return to calling ISR
diff --git a/ports/risc-v32/gnu/src/tx_thread_schedule.S b/ports/risc-v32/gnu/src/tx_thread_schedule.S
index 79bfd51a..7d2c4191 100644
--- a/ports/risc-v32/gnu/src/tx_thread_schedule.S
+++ b/ports/risc-v32/gnu/src/tx_thread_schedule.S
@@ -21,6 +21,11 @@
.section .text
+
+#include "tx_port.h"
+ .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
+ .equ TX_SOL_FRAME_SIZE, TX_RISCV_SOL_FRAME_SIZE
+
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
@@ -130,86 +135,49 @@ _tx_thread_ready_to_run:
/* if (*sp)
{ */
- lw t0, 0(sp) // Pickup stack type
+ lw t0, 0*4(sp) // Pickup stack type
beqz t0, _tx_thread_solicited_return // If 0, solicited return
/* Recover floating point registers only if saved mstatus.FS was not Off. */
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
lw t1, 29*4(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
-#if defined(__riscv_float_abi_single)
- flw f0, 31*4(sp) // Recover ft0
- flw f1, 32*4(sp) // Recover ft1
- flw f2, 33*4(sp) // Recover ft2
- flw f3, 34*4(sp) // Recover ft3
- flw f4, 35*4(sp) // Recover ft4
- flw f5, 36*4(sp) // Recover ft5
- flw f6, 37*4(sp) // Recover ft6
- flw f7, 38*4(sp) // Recover ft7
- flw f8, 39*4(sp) // Recover fs0
- flw f9, 40*4(sp) // Recover fs1
- flw f10, 41*4(sp) // Recover fa0
- flw f11, 42*4(sp) // Recover fa1
- flw f12, 43*4(sp) // Recover fa2
- flw f13, 44*4(sp) // Recover fa3
- flw f14, 45*4(sp) // Recover fa4
- flw f15, 46*4(sp) // Recover fa5
- flw f16, 47*4(sp) // Recover fa6
- flw f17, 48*4(sp) // Recover fa7
- flw f18, 49*4(sp) // Recover fs2
- flw f19, 50*4(sp) // Recover fs3
- flw f20, 51*4(sp) // Recover fs4
- flw f21, 52*4(sp) // Recover fs5
- flw f22, 53*4(sp) // Recover fs6
- flw f23, 54*4(sp) // Recover fs7
- flw f24, 55*4(sp) // Recover fs8
- flw f25, 56*4(sp) // Recover fs9
- flw f26, 57*4(sp) // Recover fs10
- flw f27, 58*4(sp) // Recover fs11
- flw f28, 59*4(sp) // Recover ft8
- flw f29, 60*4(sp) // Recover ft9
- flw f30, 61*4(sp) // Recover ft10
- flw f31, 62*4(sp) // Recover ft11
- lw t0, 63*4(sp) // Recover fcsr
+ fld f0, 16*8(sp) // Recover ft0
+ fld f1, 17*8(sp) // Recover ft1
+ fld f2, 18*8(sp) // Recover ft2
+ fld f3, 19*8(sp) // Recover ft3
+ fld f4, 20*8(sp) // Recover ft4
+ fld f5, 21*8(sp) // Recover ft5
+ fld f6, 22*8(sp) // Recover ft6
+ fld f7, 23*8(sp) // Recover ft7
+ fld f8, 24*8(sp) // Recover fs0
+ fld f9, 25*8(sp) // Recover fs1
+ fld f10, 26*8(sp) // Recover fa0
+ fld f11, 27*8(sp) // Recover fa1
+ fld f12, 28*8(sp) // Recover fa2
+ fld f13, 29*8(sp) // Recover fa3
+ fld f14, 30*8(sp) // Recover fa4
+ fld f15, 31*8(sp) // Recover fa5
+ fld f16, 32*8(sp) // Recover fa6
+ fld f17, 33*8(sp) // Recover fa7
+ fld f18, 34*8(sp) // Recover fs2
+ fld f19, 35*8(sp) // Recover fs3
+ fld f20, 36*8(sp) // Recover fs4
+ fld f21, 37*8(sp) // Recover fs5
+ fld f22, 38*8(sp) // Recover fs6
+ fld f23, 39*8(sp) // Recover fs7
+ fld f24, 40*8(sp) // Recover fs8
+ fld f25, 41*8(sp) // Recover fs9
+ fld f26, 42*8(sp) // Recover fs10
+ fld f27, 43*8(sp) // Recover fs11
+ fld f28, 44*8(sp) // Recover ft8
+ fld f29, 45*8(sp) // Recover ft9
+ fld f30, 46*8(sp) // Recover ft10
+ fld f31, 47*8(sp) // Recover ft11
+ lw t0, 96*4(sp) // Recover fcsr
csrw fcsr, t0 // Restore fcsr
-#elif defined(__riscv_float_abi_double)
- fld f0, 31*4(sp) // Recover ft0
- fld f1, 32*4(sp) // Recover ft1
- fld f2, 33*4(sp) // Recover ft2
- fld f3, 34*4(sp) // Recover ft3
- fld f4, 35*4(sp) // Recover ft4
- fld f5, 36*4(sp) // Recover ft5
- fld f6, 37*4(sp) // Recover ft6
- fld f7, 38*4(sp) // Recover ft7
- fld f8, 39*4(sp) // Recover fs0
- fld f9, 40*4(sp) // Recover fs1
- fld f10, 41*4(sp) // Recover fa0
- fld f11, 42*4(sp) // Recover fa1
- fld f12, 43*4(sp) // Recover fa2
- fld f13, 44*4(sp) // Recover fa3
- fld f14, 45*4(sp) // Recover fa4
- fld f15, 46*4(sp) // Recover fa5
- fld f16, 47*4(sp) // Recover fa6
- fld f17, 48*4(sp) // Recover fa7
- fld f18, 49*4(sp) // Recover fs2
- fld f19, 50*4(sp) // Recover fs3
- fld f20, 51*4(sp) // Recover fs4
- fld f21, 52*4(sp) // Recover fs5
- fld f22, 53*4(sp) // Recover fs6
- fld f23, 54*4(sp) // Recover fs7
- fld f24, 55*4(sp) // Recover fs8
- fld f25, 56*4(sp) // Recover fs9
- fld f26, 57*4(sp) // Recover fs10
- fld f27, 58*4(sp) // Recover fs11
- fld f28, 59*4(sp) // Recover ft8
- fld f29, 60*4(sp) // Recover ft9
- fld f30, 61*4(sp) // Recover ft10
- fld f31, 62*4(sp) // Recover ft11
- lw t0, 63*4(sp) // Recover fcsr
- csrw fcsr, t0 // Restore fcsr
-#endif
_tx_thread_schedule_skip_fp_restore:
#endif
@@ -218,7 +186,7 @@ _tx_thread_schedule_skip_fp_restore:
csrw mepc, t0 // Setup mepc
li t0, 0x1880 // Prepare mstatus: MPP=Machine(0x1800) | MPIE(0x80)
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
li t1, 0x2000 // Set FS bits for FP state
or t0, t0, t1
#endif
@@ -253,52 +221,31 @@ _tx_thread_schedule_skip_fp_restore:
lw x26, 2*4(sp) // Recover s10
lw x27, 1*4(sp) // Recover s11
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- addi sp, sp, 65*4 // Recover stack frame - with floating point enabled
-#else
- addi sp, sp, 32*4 // Recover stack frame - without floating point enabled
-#endif
+ addi sp, sp, TX_TRAP_FRAME_SIZE // Recover stack frame
mret // Return to thread
_tx_thread_solicited_return:
/* Recover floating point registers only if saved mstatus.FS was not Off. */
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
lw t1, 14*4(sp) // Pickup saved mstatus
srli t1, t1, 13
andi t1, t1, 0x3
beqz t1, _tx_thread_schedule_solicited_skip_fp_restore // Skip if FS was Off
-#if defined(__riscv_float_abi_single)
- flw f8, 15*4(sp) // Recover fs0
- flw f9, 16*4(sp) // Recover fs1
- flw f18, 17*4(sp) // Recover fs2
- flw f19, 18*4(sp) // Recover fs3
- flw f20, 19*4(sp) // Recover fs4
- flw f21, 20*4(sp) // Recover fs5
- flw f22, 21*4(sp) // Recover fs6
- flw f23, 22*4(sp) // Recover fs7
- flw f24, 23*4(sp) // Recover fs8
- flw f25, 24*4(sp) // Recover fs9
- flw f26, 25*4(sp) // Recover fs10
- flw f27, 26*4(sp) // Recover fs11
- lw t0, 27*4(sp) // Recover fcsr
- csrw fcsr, t0 // Restore fcsr
-#elif defined(__riscv_float_abi_double)
- fld f8, 15*4(sp) // Recover fs0
- fld f9, 16*4(sp) // Recover fs1
- fld f18, 17*4(sp) // Recover fs2
- fld f19, 18*4(sp) // Recover fs3
- fld f20, 19*4(sp) // Recover fs4
- fld f21, 20*4(sp) // Recover fs5
- fld f22, 21*4(sp) // Recover fs6
- fld f23, 22*4(sp) // Recover fs7
- fld f24, 23*4(sp) // Recover fs8
- fld f25, 24*4(sp) // Recover fs9
- fld f26, 25*4(sp) // Recover fs10
- fld f27, 26*4(sp) // Recover fs11
- lw t0, 27*4(sp) // Recover fcsr
+ fld f8, 8*8(sp) // Recover fs0
+ fld f9, 9*8(sp) // Recover fs1
+ fld f18, 10*8(sp) // Recover fs2
+ fld f19, 11*8(sp) // Recover fs3
+ fld f20, 12*8(sp) // Recover fs4
+ fld f21, 13*8(sp) // Recover fs5
+ fld f22, 14*8(sp) // Recover fs6
+ fld f23, 15*8(sp) // Recover fs7
+ fld f24, 16*8(sp) // Recover fs8
+ fld f25, 17*8(sp) // Recover fs9
+ fld f26, 18*8(sp) // Recover fs10
+ fld f27, 19*8(sp) // Recover fs11
+ lw t0, 40*4(sp) // Recover fcsr
csrw fcsr, t0 // Restore fcsr
-#endif
_tx_thread_schedule_solicited_skip_fp_restore:
#endif
@@ -320,11 +267,7 @@ _tx_thread_schedule_solicited_skip_fp_restore:
lw x26, 2*4(sp) // Recover s10
lw x27, 1*4(sp) // Recover s11
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- addi sp, sp, 29*4 // Recover stack frame - with floating point enabled
-#else
- addi sp, sp, 16*4 // Recover stack frame - without floating point enabled
-#endif
+ addi sp, sp, TX_SOL_FRAME_SIZE // Recover stack frame
ret // Return to thread
/* } */
diff --git a/ports/risc-v32/gnu/src/tx_thread_stack_build.S b/ports/risc-v32/gnu/src/tx_thread_stack_build.S
index 4ade60ca..3f7ae5d6 100644
--- a/ports/risc-v32/gnu/src/tx_thread_stack_build.S
+++ b/ports/risc-v32/gnu/src/tx_thread_stack_build.S
@@ -21,6 +21,10 @@
.section .text
+
+#include "tx_port.h"
+ .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
+
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
@@ -92,42 +96,14 @@ _tx_thread_stack_build:
x11 26 Initial a1
x10 27 Initial a0
x1 28 Initial ra
- -- 29 reserved
+ mstatus 29 Initial mstatus seed
mepc 30 Initial mepc
-If floating point support:
- f0 31 Initial ft0
- f1 32 Initial ft1
- f2 33 Initial ft2
- f3 34 Initial ft3
- f4 35 Initial ft4
- f5 36 Initial ft5
- f6 37 Initial ft6
- f7 38 Initial ft7
- f8 39 Initial fs0
- f9 40 Initial fs1
- f10 41 Initial fa0
- f11 42 Initial fa1
- f12 43 Initial fa2
- f13 44 Initial fa3
- f14 45 Initial fa4
- f15 46 Initial fa5
- f16 47 Initial fa6
- f17 48 Initial fa7
- f18 49 Initial fs2
- f19 50 Initial fs3
- f20 51 Initial fs4
- f21 52 Initial fs5
- f22 53 Initial fs6
- f23 54 Initial fs7
- f24 55 Initial fs8
- f25 56 Initial fs9
- f26 57 Initial fs10
- f27 58 Initial fs11
- f28 59 Initial ft8
- f29 60 Initial ft9
- f30 61 Initial ft10
- f31 62 Initial ft11
- fscr 63 Initial fscr
+ -- 31 pad (0)
+ If floating point support (double ABI), byte offsets from
+ the frame base:
+ f0-f31 128 + i*8 Initial FP registers (all 0)
+ fcsr 384 Initial fcsr (0)
+ pad 388-399 (0)
Stack Bottom: (higher memory address) */
@@ -137,11 +113,7 @@ If floating point support:
/* Actually build the stack frame. */
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- addi t0, t0, -65*4
-#else
- addi t0, t0, -32*4 // Allocate space for the stack frame
-#endif
+ addi t0, t0, -TX_TRAP_FRAME_SIZE // Allocate space for the stack frame
li t1, 1 // Build stack type
sw t1, 0*4(t0) // Place stack type on the top
sw zero, 1*4(t0) // Initial s11
@@ -173,44 +145,22 @@ If floating point support:
sw zero, 27*4(t0) // Initial a0
sw zero, 28*4(t0) // Initial ra
sw a1, 30*4(t0) // Initial mepc (thread entry point)
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- sw zero, 31*4(t0) // Initial ft0
- sw zero, 32*4(t0) // Initial ft1
- sw zero, 33*4(t0) // Initial ft2
- sw zero, 34*4(t0) // Initial ft3
- sw zero, 35*4(t0) // Initial ft4
- sw zero, 36*4(t0) // Initial ft5
- sw zero, 37*4(t0) // Initial ft6
- sw zero, 38*4(t0) // Initial ft7
- sw zero, 39*4(t0) // Initial fs0
- sw zero, 40*4(t0) // Initial fs1
- sw zero, 41*4(t0) // Initial fa0
- sw zero, 42*4(t0) // Initial fa1
- sw zero, 43*4(t0) // Initial fa2
- sw zero, 44*4(t0) // Initial fa3
- sw zero, 45*4(t0) // Initial fa4
- sw zero, 46*4(t0) // Initial fa5
- sw zero, 47*4(t0) // Initial fa6
- sw zero, 48*4(t0) // Initial fa7
- sw zero, 49*4(t0) // Initial fs2
- sw zero, 50*4(t0) // Initial fs3
- sw zero, 51*4(t0) // Initial fs4
- sw zero, 52*4(t0) // Initial fs5
- sw zero, 53*4(t0) // Initial fs6
- sw zero, 54*4(t0) // Initial fs7
- sw zero, 55*4(t0) // Initial fs8
- sw zero, 56*4(t0) // Initial fs9
- sw zero, 57*4(t0) // Initial fs10
- sw zero, 58*4(t0) // Initial fs11
- sw zero, 59*4(t0) // Initial ft8
- sw zero, 60*4(t0) // Initial ft9
- sw zero, 61*4(t0) // Initial ft10
- sw zero, 62*4(t0) // Initial ft11
- csrr a1, fcsr // Read fcsr for initial value
- sw a1, 63*4(t0) // Initial fcsr
- sw zero, 64*4(t0) // Reserved word (0)
+ sw zero, 31*4(t0) // Pad word (0)
+#if defined(__riscv_float_abi_double)
+ li t1, 0x6000 // mstatus seed: FS = Dirty (bits 14:13 = 11)
+ sw t1, 29*4(t0) // so the first dispatch restores the clean FP state
+
+ /* Zero the whole FP area: f0-f31 double slots (128-383), fcsr (384)
+ and the pad words up to the frame end. Threads start with clean
+ FP registers and fcsr = 0. */
+ addi t1, t0, 32*4 // First FP-area word (offset 128)
+ addi t2, t0, TX_TRAP_FRAME_SIZE // One past the frame end (offset 400)
+_tx_thread_stack_build_fp_zero:
+ sw zero, 0(t1) // Clear FP-area word
+ addi t1, t1, 4
+ bne t1, t2, _tx_thread_stack_build_fp_zero
#else
- sw zero, 31*4(t0) // Reserved word (0)
+ sw zero, 29*4(t0) // Initial mstatus slot (0, no FP gate)
#endif
/* Setup stack pointer. */
diff --git a/ports/risc-v32/gnu/src/tx_thread_system_return.S b/ports/risc-v32/gnu/src/tx_thread_system_return.S
index 20ff25f2..2e49bc3f 100644
--- a/ports/risc-v32/gnu/src/tx_thread_system_return.S
+++ b/ports/risc-v32/gnu/src/tx_thread_system_return.S
@@ -21,6 +21,10 @@
.section .text
+
+#include "tx_port.h"
+ .equ TX_SOL_FRAME_SIZE, TX_RISCV_SOL_FRAME_SIZE
+
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
@@ -63,54 +67,33 @@ _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*4 // Allocate space on the stack - with floating point enabled
-#else
- addi sp, sp, -16*4 // Allocate space on the stack - without floating point enabled
-#endif
+ addi sp, sp, -TX_SOL_FRAME_SIZE // Allocate the solicited stack frame
- sw zero, 0(sp) // Solicited stack type (store early so slot is always written)
+ sw zero, 0*4(sp) // Solicited stack type (store early so slot is always written)
/* Read and save mstatus first; use it to guard FP register save. */
csrr t0, mstatus // Pickup current mstatus
sw t0, 14*4(sp) // Save mstatus
/* Store floating point preserved registers — only if FS != Off. */
-#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
+#if defined(__riscv_float_abi_double)
srli t1, t0, 13 // Shift FS field to bits [1:0]
andi t1, t1, 0x3 // Isolate FS bits
beqz t1, _tx_thread_system_return_skip_fp // Skip FP save if FS == Off
-#if defined(__riscv_float_abi_single)
- fsw f8, 15*4(sp) // Store fs0
- fsw f9, 16*4(sp) // Store fs1
- fsw f18, 17*4(sp) // Store fs2
- fsw f19, 18*4(sp) // Store fs3
- fsw f20, 19*4(sp) // Store fs4
- fsw f21, 20*4(sp) // Store fs5
- fsw f22, 21*4(sp) // Store fs6
- fsw f23, 22*4(sp) // Store fs7
- fsw f24, 23*4(sp) // Store fs8
- fsw f25, 24*4(sp) // Store fs9
- fsw f26, 25*4(sp) // Store fs10
- fsw f27, 26*4(sp) // Store fs11
+ fsd f8, 8*8(sp) // Store fs0
+ fsd f9, 9*8(sp) // Store fs1
+ fsd f18, 10*8(sp) // Store fs2
+ fsd f19, 11*8(sp) // Store fs3
+ fsd f20, 12*8(sp) // Store fs4
+ fsd f21, 13*8(sp) // Store fs5
+ fsd f22, 14*8(sp) // Store fs6
+ fsd f23, 15*8(sp) // Store fs7
+ fsd f24, 16*8(sp) // Store fs8
+ fsd f25, 17*8(sp) // Store fs9
+ fsd f26, 18*8(sp) // Store fs10
+ fsd f27, 19*8(sp) // Store fs11
csrr t0, fcsr
- sw t0, 27*4(sp) // Store fcsr
-#elif defined(__riscv_float_abi_double)
- fsd f8, 15*4(sp) // Store fs0
- fsd f9, 16*4(sp) // Store fs1
- fsd f18, 17*4(sp) // Store fs2
- fsd f19, 18*4(sp) // Store fs3
- fsd f20, 19*4(sp) // Store fs4
- fsd f21, 20*4(sp) // Store fs5
- fsd f22, 21*4(sp) // Store fs6
- fsd f23, 22*4(sp) // Store fs7
- fsd f24, 23*4(sp) // Store fs8
- fsd f25, 24*4(sp) // Store fs9
- fsd f26, 25*4(sp) // Store fs10
- fsd f27, 26*4(sp) // Store fs11
- csrr t0, fcsr
- sw t0, 27*4(sp) // Store fcsr
-#endif
+ sw t0, 40*4(sp) // Store fcsr
_tx_thread_system_return_skip_fp:
#endif
diff --git a/ports/risc-v32/gnu/src/tx_timer_interrupt.S b/ports/risc-v32/gnu/src/tx_timer_interrupt.S
index d3b2fc60..8b5d7df3 100644
--- a/ports/risc-v32/gnu/src/tx_timer_interrupt.S
+++ b/ports/risc-v32/gnu/src/tx_timer_interrupt.S
@@ -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. */