From 651ebdc2c6f9965cf2e0d672e43f843bdc4806aa Mon Sep 17 00:00:00 2001 From: Akif Ejaz Date: Thu, 3 Sep 2026 13:58:03 +0500 Subject: [PATCH 1/2] riscv32: spec compliance and regression test fix Signed-off-by: Akif Ejaz --- cmake/riscv32-unknown-elf.cmake | 2 +- cmake/riscv32_gnu.cmake | 2 +- .../gnu/example_build/qemu_virt/.gitignore | 4 + .../example_build/qemu_virt/CMakeLists.txt | 24 +- .../gnu/example_build/qemu_virt/board.c | 14 +- .../example_build/qemu_virt/demo_threadx.c | 4 + .../gnu/example_build/qemu_virt/hwtimer.c | 69 ++++- .../gnu/example_build/qemu_virt/hwtimer.h | 4 +- .../gnu/example_build/qemu_virt/link.lds | 21 +- .../test/threadx_test_tx_gnu_riscv32_qemu.py | 275 ++++++++++++------ .../qemu_virt/tx_initialize_low_level.S | 22 +- ports/risc-v32/gnu/inc/tx_port.h | 34 ++- .../gnu/src/tx_initialize_low_level.S | 8 +- .../gnu/src/tx_thread_context_restore.S | 208 +++++-------- .../risc-v32/gnu/src/tx_thread_context_save.S | 157 ++++------ ports/risc-v32/gnu/src/tx_thread_schedule.S | 175 ++++------- .../risc-v32/gnu/src/tx_thread_stack_build.S | 107 ++----- .../gnu/src/tx_thread_system_return.S | 60 ++-- ports/risc-v32/gnu/src/tx_timer_interrupt.S | 4 +- test/tx/cmake/riscv/bsp/entry.S | 6 + test/tx/cmake/riscv/bsp/hwtimer.c | 84 +++++- test/tx/cmake/riscv/bsp/hwtimer.h | 4 +- test/tx/cmake/riscv/bsp/link.lds | 32 +- test/tx/cmake/riscv/bsp/plic.c | 25 +- test/tx/cmake/riscv/bsp/syscalls.c | 49 +++- test/tx/cmake/riscv/bsp/trap.c | 3 - .../cmake/riscv/bsp/tx_initialize_low_level.S | 29 +- 27 files changed, 793 insertions(+), 633 deletions(-) create mode 100644 ports/risc-v32/gnu/example_build/qemu_virt/.gitignore diff --git a/cmake/riscv32-unknown-elf.cmake b/cmake/riscv32-unknown-elf.cmake index d21e729ca..b428dc8e3 100644 --- a/cmake/riscv32-unknown-elf.cmake +++ b/cmake/riscv32-unknown-elf.cmake @@ -20,7 +20,7 @@ set(CMAKE_CXX_FLAGS "${CXXFLAGS}" CACHE INTERNAL "cxx compiler flags") if(DEFINED SOFT_FLOAT) set(CMAKE_ASM_FLAGS "${ASFLAGS} -D__ASSEMBLER__" CACHE INTERNAL "asm compiler flags") else() - set(CMAKE_ASM_FLAGS "${ASFLAGS} -D__ASSEMBLER__ -D__riscv_float_abi_single" CACHE INTERNAL "asm compiler flags") + set(CMAKE_ASM_FLAGS "${ASFLAGS} -D__ASSEMBLER__" CACHE INTERNAL "asm compiler flags") endif() set(CMAKE_EXE_LINKER_FLAGS "${LDFLAGS}" CACHE INTERNAL "exe link flags") diff --git a/cmake/riscv32_gnu.cmake b/cmake/riscv32_gnu.cmake index 6843423c5..584cbf0ed 100644 --- a/cmake/riscv32_gnu.cmake +++ b/cmake/riscv32_gnu.cmake @@ -5,7 +5,7 @@ set(THREADX_ARCH "risc-v32") set(THREADX_TOOLCHAIN "gnu") if(DEFINED SOFT_FLOAT) set(ARCH_FLAGS "-g -march=rv32ima_zicsr -mabi=ilp32 -mcmodel=medany") - set(CACHE{SOFT_FLOAT} FORCE 1) + set(SOFT_FLOAT 1 CACHE BOOL "build the soft-float rv32ima_zicsr/ilp32 configuration" FORCE) else() set(ARCH_FLAGS "-g -march=rv32gc -mabi=ilp32d -mcmodel=medany -mrelax") endif() 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 000000000..18a4075cc --- /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 43e757ace..cbf67798e 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 $ - --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 fc75de668..4d0056a81 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 #include -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> 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 966b1abfb..7bc2d0e3a 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 -#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 d2ef5f289..42f5d3662 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 6123cd73b..e0b99ad43 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 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 @@ def run_qemu_test(elf_path, qemu_bin, gdb_bin): 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 @@ def run_qemu_test(elf_path, qemu_bin, gdb_bin): 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,42 @@ def run_qemu_test(elf_path, qemu_bin, gdb_bin): 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 + fpu_verified = skip_fpu + 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.") + 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 +345,21 @@ def run_qemu_test(elf_path, qemu_bin, gdb_bin): 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 +375,9 @@ def run_qemu_test(elf_path, qemu_bin, gdb_bin): 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 +387,12 @@ def run_qemu_test(elf_path, qemu_bin, gdb_bin): 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 9a7a74ffd..5e733ab23 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,15 @@ .global trap_entry .extern trap_handler .extern _tx_thread_context_restore - 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) +/* 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). */ +#if defined(__riscv_float_abi_double) + .equ TX_TRAP_FRAME_SIZE, 400 #else - addi sp, sp, -128 // Allocate space for all registers - without floating point enabled (32*4) + .equ TX_TRAP_FRAME_SIZE, 128 #endif + trap_entry: + 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 +76,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 +131,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 +149,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 c990aa970..39561ae41 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,38 @@ #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 + /* Include shared RISC-V32 port definitions common to all toolchain ports. */ #include "../../common/tx_port_riscv32_common.h" 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 2940f2159..b7ebc1b51 100644 --- a/ports/risc-v32/gnu/src/tx_initialize_low_level.S +++ b/ports/risc-v32/gnu/src/tx_initialize_low_level.S @@ -62,7 +62,7 @@ __tx_free_memory_start: /**************************************************************************/ /* VOID _tx_initialize_low_level(VOID) { */ - .weak _tx_initialize_low_level + .global _tx_initialize_low_level .weak _tx_initialize_low_level _tx_initialize_low_level: @@ -79,8 +79,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 d3d449e47..84ba7e3ff 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,17 @@ /**************************************************************************/ .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. */ +#if defined(__riscv_float_abi_double) + .equ TX_TRAP_FRAME_SIZE, 400 +#else + .equ TX_TRAP_FRAME_SIZE, 128 +#endif + /**************************************************************************/ /* */ /* FUNCTION RELEASE */ @@ -84,57 +95,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 +152,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 +176,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 +195,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 +213,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 +261,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 +285,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 +300,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 0a0c0c156..c8175b053 100644 --- a/ports/risc-v32/gnu/src/tx_thread_context_save.S +++ b/ports/risc-v32/gnu/src/tx_thread_context_save.S @@ -20,6 +20,13 @@ /**************************************************************************/ .section .text + +#if defined(__riscv_float_abi_double) + .equ TX_TRAP_FRAME_SIZE, 400 +#else + .equ TX_TRAP_FRAME_SIZE, 128 +#endif + /**************************************************************************/ /* */ /* FUNCTION RELEASE */ @@ -61,7 +68,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 +107,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 + /* 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 -#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 - csrr t0, fcsr - sw t0, 63*4(sp) // Store fcsr + sw t0, 96*4(sp) // Store fcsr #endif _tx_thread_skip_fpu_save: @@ -200,59 +182,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: @@ -277,9 +234,5 @@ _tx_thread_idle_system_save: /* } } */ -#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 79bfd51a7..5c3f06a24 100644 --- a/ports/risc-v32/gnu/src/tx_thread_schedule.S +++ b/ports/risc-v32/gnu/src/tx_thread_schedule.S @@ -21,6 +21,15 @@ .section .text + +#if defined(__riscv_float_abi_double) + .equ TX_TRAP_FRAME_SIZE, 400 + .equ TX_SOL_FRAME_SIZE, 176 +#else + .equ TX_TRAP_FRAME_SIZE, 128 + .equ TX_SOL_FRAME_SIZE, 128 +#endif + /**************************************************************************/ /* */ /* FUNCTION RELEASE */ @@ -130,86 +139,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 - 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 + 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 -#endif _tx_thread_schedule_skip_fp_restore: #endif @@ -218,7 +190,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 +225,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 +271,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 4ade60ca6..d80aa3ab0 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,13 @@ .section .text + +#if defined(__riscv_float_abi_double) + .equ TX_TRAP_FRAME_SIZE, 400 +#else + .equ TX_TRAP_FRAME_SIZE, 128 +#endif + /**************************************************************************/ /* */ /* FUNCTION RELEASE */ @@ -92,42 +99,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 +116,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 +148,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 20ff25f21..7c7924565 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,13 @@ .section .text + +#if defined(__riscv_float_abi_double) + .equ TX_SOL_FRAME_SIZE, 176 +#else + .equ TX_SOL_FRAME_SIZE, 128 +#endif + /**************************************************************************/ /* */ /* FUNCTION RELEASE */ @@ -63,54 +70,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 d3b2fc603..8b5d7df3d 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. */ diff --git a/test/tx/cmake/riscv/bsp/entry.S b/test/tx/cmake/riscv/bsp/entry.S index 2f16c0525..5abe98523 100644 --- a/test/tx/cmake/riscv/bsp/entry.S +++ b/test/tx/cmake/riscv/bsp/entry.S @@ -18,6 +18,7 @@ .extern _sysstack_end .extern _bss_start .extern _bss_end +.extern __global_pointer$ _start: csrr t0, mhartid @@ -56,6 +57,11 @@ _start: li x30, 0 li x31, 0 + .option push + .option norelax + la gp, __global_pointer$ + .option pop + /* Set up stack pointer from linker symbol. */ la sp, _sysstack_end diff --git a/test/tx/cmake/riscv/bsp/hwtimer.c b/test/tx/cmake/riscv/bsp/hwtimer.c index 99821f1ee..8b81268a9 100644 --- a/test/tx/cmake/riscv/bsp/hwtimer.c +++ b/test/tx/cmake/riscv/bsp/hwtimer.c @@ -9,25 +9,99 @@ **************************************************************************/ #include "csr.h" +#include "tx_api.h" #include "hwtimer.h" -#include #define CLINT (0x02000000L) #define CLINT_TIME (CLINT + 0xBFF8) #define CLINT_TIMECMP(id) (CLINT + 0x4000 + 8 * (id)) +#if __riscv_xlen == 32 + +/* 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(id) (*(volatile uint32_t *)(CLINT_TIMECMP(id))) +#define MTIMECMP_HI(id) (*(volatile uint32_t *)(CLINT_TIMECMP(id) + 4)) + +/* RV32 mtime read: 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(uintptr_t 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(uintptr_t hart, uint64_t value) +{ + MTIMECMP_LO(hart) = 0xFFFFFFFF; + MTIMECMP_HI(hart) = (uint32_t)(value >> 32); + MTIMECMP_LO(hart) = (uint32_t)value; +} + +#else /* __riscv_xlen == 64 */ + +/* RV64: naturally aligned 64-bit MMIO accesses are single loads/stores. */ +static uint64_t clint_time_read(void) +{ + return *(volatile uint64_t *)CLINT_TIME; +} + +static uint64_t clint_timecmp_read(uintptr_t hart) +{ + return *(volatile uint64_t *)CLINT_TIMECMP(hart); +} + +static void clint_timecmp_write(uintptr_t hart, uint64_t value) +{ + *(volatile uint64_t *)CLINT_TIMECMP(hart) = value; +} + +#endif /* __riscv_xlen */ + int hwtimer_init(void) { uintptr_t hart = riscv_get_core(); - uint64_t time = *((volatile uint64_t *)CLINT_TIME); - *((volatile uint64_t *)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER; + + clint_timecmp_write(hart, clint_time_read() + TICKNUM_PER_TIMER); return 0; } int hwtimer_handler(void) { uintptr_t hart = riscv_get_core(); - uint64_t time = *((volatile uint64_t *)CLINT_TIME); - *((volatile uint64_t *)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER; + + /* Absolute re-arm: advance from the previous compare value, so trap + latency does not accumulate as tick drift; catch up if the next + compare already passed. */ + 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/test/tx/cmake/riscv/bsp/hwtimer.h b/test/tx/cmake/riscv/bsp/hwtimer.h index e6b9efa4e..bde0bba56 100644 --- a/test/tx/cmake/riscv/bsp/hwtimer.h +++ b/test/tx/cmake/riscv/bsp/hwtimer.h @@ -13,8 +13,8 @@ #include -#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/test/tx/cmake/riscv/bsp/link.lds b/test/tx/cmake/riscv/bsp/link.lds index 42f08fb6e..717530b3b 100644 --- a/test/tx/cmake/riscv/bsp/link.lds +++ b/test/tx/cmake/riscv/bsp/link.lds @@ -12,6 +12,12 @@ MEMORY RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M } +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + SECTIONS { . = 0x80000000; @@ -21,23 +27,24 @@ SECTIONS *(.text .text.*) . = ALIGN(0x1000); PROVIDE(etext = .); - } > RAM + } > RAM :text .rodata : { . = ALIGN(16); *(.srodata .srodata.*) . = ALIGN(16); *(.rodata .rodata.*) - } > RAM + } > RAM :text .data : { - . = ALIGN(16); + . = ALIGN(0x1000); + PROVIDE(__global_pointer$ = . + 0x800); *(.sdata .sdata.*) . = ALIGN(16); *(.data .data.*) - } > RAM + } > RAM :data - .bss : { + .bss (NOLOAD) : { . = ALIGN(16); _bss_start = .; *(.sbss .sbss.*) @@ -45,14 +52,23 @@ SECTIONS *(.bss .bss.*) *(COMMON) _bss_end = .; - } > RAM + } > RAM :data - .stack : { + .stack (NOLOAD) : { . = ALIGN(4096); _sysstack_start = .; . += 0x4000; _sysstack_end = .; - } > RAM + } > RAM :data + + /* Fixed 64 KiB newlib sbrk heap window. _end stays after it, so the + ThreadX first-unused-memory pointer starts past the heap. */ + .heap (NOLOAD) : { + . = ALIGN(16); + _heap_start = .; + . += 0x10000; + _heap_end = .; + } > RAM :data PROVIDE(_end = .); diff --git a/test/tx/cmake/riscv/bsp/plic.c b/test/tx/cmake/riscv/bsp/plic.c index a2b44feca..16dad815f 100644 --- a/test/tx/cmake/riscv/bsp/plic.c +++ b/test/tx/cmake/riscv/bsp/plic.c @@ -52,32 +52,47 @@ int plic_unregister_callback(int irqno) int plic_init(void) { + uintptr_t 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. */ + *(volatile uint32_t *)PLIC_MPRIORITY(hart) = 0; + for (int w = 0; w < MAX_CALLBACK_NUM / 32; w++) + *(volatile uint32_t *)(PLIC_MENABLE(hart) + w * 4) = 0; + return 0; } int plic_claim(void) { uintptr_t hart = riscv_get_core(); - return (int)(*(uint32_t *)PLIC_MCLAIM(hart)); + return (int)(*(volatile uint32_t *)PLIC_MCLAIM(hart)); } void plic_complete(int irqno) { uintptr_t 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 || irqno >= MAX_CALLBACK_NUM) { - plic_complete(irqno); - return 0; // spurious or out-of-range, not an error + + 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); diff --git a/test/tx/cmake/riscv/bsp/syscalls.c b/test/tx/cmake/riscv/bsp/syscalls.c index 802fe8afa..40ca86f32 100644 --- a/test/tx/cmake/riscv/bsp/syscalls.c +++ b/test/tx/cmake/riscv/bsp/syscalls.c @@ -15,6 +15,7 @@ #include #include #include +#include #define UART0_THR (*(volatile unsigned char *)0x10000000L) #define UART0_LSR (*(volatile unsigned char *)0x10000005L) @@ -25,6 +26,39 @@ #define VIRT_TEST_PASS 0x5555u #define VIRT_TEST_FAIL 0x3333u +#define QEMU_TIMEBASE_HZ 10000000ULL + +static uint64_t read_time_counter(void) +{ +#if __riscv_xlen == 64 + uint64_t value; + __asm__ volatile("rdtime %0" : "=r" (value)); + return value; +#else + uint32_t high; + uint32_t low; + uint32_t high_check; + + do + { + __asm__ volatile("rdtimeh %0" : "=r" (high)); + __asm__ volatile("rdtime %0" : "=r" (low)); + __asm__ volatile("rdtimeh %0" : "=r" (high_check)); + } while (high != high_check); + + return ((uint64_t)high << 32) | low; +#endif +} + +time_t time(time_t *timer) +{ + time_t seconds = (time_t)(read_time_counter() / QEMU_TIMEBASE_HZ); + + if (timer != NULL) + *timer = seconds; + return seconds; +} + /* Called by testcontrol.c when EXTERNAL_EXIT is defined. */ __attribute__((noreturn)) void external_exit(unsigned int code) { @@ -50,13 +84,24 @@ int _write(int fd, const char *buf, int count) return count; } -extern char _end[]; +/* Fixed 64 KiB heap window from the linker script. It keeps the newlib + heap out of the ThreadX first-unused-memory area, which starts at + _end (placed after _heap_end). */ +extern char _heap_start[]; +extern char _heap_end[]; static char *heap_ptr = 0; void *_sbrk(int incr) { if (heap_ptr == 0) - heap_ptr = _end; + heap_ptr = _heap_start; + + if ((incr > 0 && _heap_end - heap_ptr < incr) || + (incr < 0 && heap_ptr - _heap_start < -incr)) + { + errno = ENOMEM; + return (void *)-1; + } char *prev = heap_ptr; heap_ptr += incr; diff --git a/test/tx/cmake/riscv/bsp/trap.c b/test/tx/cmake/riscv/bsp/trap.c index 0ac7990ac..f54ea2ffa 100644 --- a/test/tx/cmake/riscv/bsp/trap.c +++ b/test/tx/cmake/riscv/bsp/trap.c @@ -36,9 +36,6 @@ extern void _exit(int code) __attribute__((noreturn)); void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) { - (void)mepc; - (void)mtval; - if (OS_IS_INTERUPT(mcause)) { if (OS_IS_TICK_INT(mcause)) diff --git a/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S b/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S index 8d474d66b..73587ffef 100644 --- a/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S +++ b/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S @@ -12,6 +12,9 @@ Supports both RV32 and RV64 via __riscv_xlen conditionals. */ #include "csr.h" +#if __riscv_xlen == 32 +#include "tx_port.h" +#endif #if __riscv_xlen == 64 #define STORE sd @@ -33,13 +36,23 @@ .extern trap_handler .extern _tx_thread_context_restore -trap_entry: +/* Use a port contract when one exists. Keep legacy ports compatible. */ +#ifndef TX_RISCV_TRAP_FRAME_SIZE #if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - addi sp, sp, -(65 * REGBYTES) +#define TX_RISCV_TRAP_FRAME_SIZE (65 * REGBYTES) +#define TX_RISCV_TRAP_CALL_FRAME_SIZE REGBYTES #else - addi sp, sp, -(32 * REGBYTES) +#define TX_RISCV_TRAP_FRAME_SIZE (32 * REGBYTES) +#define TX_RISCV_TRAP_CALL_FRAME_SIZE 16 +#endif #endif + .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE + .equ TX_TRAP_CALL_FRAME_SIZE, TX_RISCV_TRAP_CALL_FRAME_SIZE + +trap_entry: + addi sp, sp, -TX_TRAP_FRAME_SIZE + STORE x1, (28 * REGBYTES)(sp) call _tx_thread_context_save @@ -47,11 +60,11 @@ trap_entry: csrr a0, mcause csrr a1, mepc csrr a2, mtval - addi sp, sp, -REGBYTES + addi sp, sp, -TX_TRAP_CALL_FRAME_SIZE STORE ra, 0(sp) call trap_handler LOAD ra, 0(sp) - addi sp, sp, REGBYTES + addi sp, sp, TX_TRAP_CALL_FRAME_SIZE call _tx_thread_context_restore @@ -80,7 +93,7 @@ _tx_initialize_low_level: csrrc zero, mstatus, t0 li t0, (MSTATUS_MPP_M | MSTATUS_MPIE) csrrs zero, mstatus, t0 - li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE) + li t0, (MIE_MTIE | MIE_MEIE) // No MSIE: software interrupts are never dispatched here csrrs zero, mie, t0 #ifdef __riscv_flen @@ -89,11 +102,11 @@ _tx_initialize_low_level: fscsr x0 #endif - addi sp, sp, -REGBYTES + addi sp, sp, -16 STORE ra, 0(sp) call board_init LOAD ra, 0(sp) - addi sp, sp, REGBYTES + addi sp, sp, 16 la t0, trap_entry csrw mtvec, t0 From a6c1d3013f1ee137deef2ac9023d9f2ddc545435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Wed, 9 Sep 2026 11:09:32 -0400 Subject: [PATCH 2/2] Derived the RISC-V32 frame sizes from the port contract in one place tx_port.h published TX_RISCV_TRAP_FRAME_SIZE for the GNU BSP assembly, but nothing in the port consumed it. Six .S files each rebuilt the same numbers from their own #if, so the interrupt frame size was written out in seven places and the solicited frame size in three. That is the shape that produced the RISC-V64 fault fixed in #708, where the port moved to a padded frame and one copy of the constant did not. The sources now include tx_port.h and take both sizes from it, and no literal frame size remains in the port. TX_RISCV_SOL_FRAME_SIZE joins the contract, since the solicited frame was never published at all. The emitted code is unchanged: 400 and 176 bytes for ILP32D, 128 for soft-float, confirmed by disassembly before and after. Two further corrections: _tx_initialize_low_level carried .global immediately followed by .weak, so the symbol stayed weak and the .global did nothing. Weak is what the port wants, because the example and regression BSPs both provide their own definition, so the stray .global is removed rather than the .weak. Verified with nm that the symbol is still W. The QEMU runner seeded fpu_verified from skip_fpu, so a soft-float run satisfied the FPU gate whether or not the script ever reported the skip. It now starts false and is set only when the skip marker is present, so a run that dies before reaching that point fails instead of passing. Assisted-by: Claude Code (Opus 5) --- .../qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py | 6 +++++- .../example_build/qemu_virt/tx_initialize_low_level.S | 7 ++----- ports/risc-v32/gnu/inc/tx_port.h | 8 ++++++++ ports/risc-v32/gnu/src/tx_initialize_low_level.S | 3 +-- ports/risc-v32/gnu/src/tx_thread_context_restore.S | 7 ++----- ports/risc-v32/gnu/src/tx_thread_context_save.S | 7 ++----- ports/risc-v32/gnu/src/tx_thread_schedule.S | 10 +++------- ports/risc-v32/gnu/src/tx_thread_stack_build.S | 7 ++----- ports/risc-v32/gnu/src/tx_thread_system_return.S | 7 ++----- 9 files changed, 27 insertions(+), 35 deletions(-) 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 e0b99ad43..bc97e7450 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 @@ -316,7 +316,7 @@ def run_qemu_test(elf_path, qemu_bin, gdb_bin, skip_fpu): stdout = gdb_process.stdout gdb_succeeded = gdb_process.returncode == 0 timer_hit = "Breakpoint 4, _tx_timer_interrupt" in stdout - fpu_verified = skip_fpu + 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 @@ -327,6 +327,10 @@ def run_qemu_test(elf_path, qemu_bin, gdb_bin, skip_fpu): 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.") 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 5e733ab23..9cda8096d 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 @@ -61,11 +61,8 @@ .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). */ -#if defined(__riscv_float_abi_double) - .equ TX_TRAP_FRAME_SIZE, 400 -#else - .equ TX_TRAP_FRAME_SIZE, 128 -#endif +#include "tx_port.h" + .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE trap_entry: addi sp, sp, -TX_TRAP_FRAME_SIZE // Allocate space for all registers (400 with FP double, 128 without) diff --git a/ports/risc-v32/gnu/inc/tx_port.h b/ports/risc-v32/gnu/inc/tx_port.h index 39561ae41..642b12750 100644 --- a/ports/risc-v32/gnu/inc/tx_port.h +++ b/ports/risc-v32/gnu/inc/tx_port.h @@ -79,6 +79,14 @@ #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" 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 b7ebc1b51..ca97ba62d 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) { */ - .global _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. */ 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 84ba7e3ff..c48546433 100644 --- a/ports/risc-v32/gnu/src/tx_thread_context_restore.S +++ b/ports/risc-v32/gnu/src/tx_thread_context_restore.S @@ -25,11 +25,8 @@ 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. */ -#if defined(__riscv_float_abi_double) - .equ TX_TRAP_FRAME_SIZE, 400 -#else - .equ TX_TRAP_FRAME_SIZE, 128 -#endif +#include "tx_port.h" + .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE /**************************************************************************/ /* */ 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 98311c18d..9a67520aa 100644 --- a/ports/risc-v32/gnu/src/tx_thread_context_save.S +++ b/ports/risc-v32/gnu/src/tx_thread_context_save.S @@ -23,11 +23,8 @@ .section .text -#if defined(__riscv_float_abi_double) - .equ TX_TRAP_FRAME_SIZE, 400 -#else - .equ TX_TRAP_FRAME_SIZE, 128 -#endif +#include "tx_port.h" + .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE /**************************************************************************/ /* */ diff --git a/ports/risc-v32/gnu/src/tx_thread_schedule.S b/ports/risc-v32/gnu/src/tx_thread_schedule.S index 5c3f06a24..7d2c41917 100644 --- a/ports/risc-v32/gnu/src/tx_thread_schedule.S +++ b/ports/risc-v32/gnu/src/tx_thread_schedule.S @@ -22,13 +22,9 @@ .section .text -#if defined(__riscv_float_abi_double) - .equ TX_TRAP_FRAME_SIZE, 400 - .equ TX_SOL_FRAME_SIZE, 176 -#else - .equ TX_TRAP_FRAME_SIZE, 128 - .equ TX_SOL_FRAME_SIZE, 128 -#endif +#include "tx_port.h" + .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE + .equ TX_SOL_FRAME_SIZE, TX_RISCV_SOL_FRAME_SIZE /**************************************************************************/ /* */ 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 d80aa3ab0..3f7ae5d6e 100644 --- a/ports/risc-v32/gnu/src/tx_thread_stack_build.S +++ b/ports/risc-v32/gnu/src/tx_thread_stack_build.S @@ -22,11 +22,8 @@ .section .text -#if defined(__riscv_float_abi_double) - .equ TX_TRAP_FRAME_SIZE, 400 -#else - .equ TX_TRAP_FRAME_SIZE, 128 -#endif +#include "tx_port.h" + .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE /**************************************************************************/ /* */ 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 7c7924565..2e49bc3fe 100644 --- a/ports/risc-v32/gnu/src/tx_thread_system_return.S +++ b/ports/risc-v32/gnu/src/tx_thread_system_return.S @@ -22,11 +22,8 @@ .section .text -#if defined(__riscv_float_abi_double) - .equ TX_SOL_FRAME_SIZE, 176 -#else - .equ TX_SOL_FRAME_SIZE, 128 -#endif +#include "tx_port.h" + .equ TX_SOL_FRAME_SIZE, TX_RISCV_SOL_FRAME_SIZE /**************************************************************************/ /* */