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