diff options
Diffstat (limited to 'ports/risc-v32/gnu/src')
| -rw-r--r-- | ports/risc-v32/gnu/src/tx_initialize_low_level.S | 112 | ||||
| -rw-r--r-- | ports/risc-v32/gnu/src/tx_thread_context_restore.S | 410 | ||||
| -rw-r--r-- | ports/risc-v32/gnu/src/tx_thread_context_save.S | 271 | ||||
| -rw-r--r-- | ports/risc-v32/gnu/src/tx_thread_interrupt_control.S | 88 | ||||
| -rw-r--r-- | ports/risc-v32/gnu/src/tx_thread_schedule.S | 318 | ||||
| -rw-r--r-- | ports/risc-v32/gnu/src/tx_thread_stack_build.S | 221 | ||||
| -rw-r--r-- | ports/risc-v32/gnu/src/tx_thread_system_return.S | 168 | ||||
| -rw-r--r-- | ports/risc-v32/gnu/src/tx_timer_interrupt.S | 204 |
8 files changed, 1792 insertions, 0 deletions
diff --git a/ports/risc-v32/gnu/src/tx_initialize_low_level.S b/ports/risc-v32/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..703466bd --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .section .data + .global __tx_free_memory_start +__tx_free_memory_start: + + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/**************************************************************************/ +/* VOID _tx_initialize_low_level(VOID) +{ */ + .global _tx_initialize_low_level + .weak _tx_initialize_low_level +_tx_initialize_low_level: + + /* Save the system stack pointer. */ + /* _tx_thread_system_stack_ptr = sp; */ + + la t0, _tx_thread_system_stack_ptr // Pickup address of system stack ptr + sw sp, 0(t0) // Save system stack pointer + + /* Pickup first free address. */ + /* _tx_initialize_unused_memory(__tx_free_memory_start); */ + + la t0, __tx_free_memory_start // Pickup first free address + 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 + li t0, 0 + csrw fcsr, t0 // Clear FP control/status register +#endif + + ret + +/* Timer Interrupt Handler Note: + Platform-specific implementations must provide their own timer ISR. + The timer interrupt handler should follow this execution flow: + + 1. Disable interrupts (if not done by hardware exception entry) + 2. Allocate interrupt stack frame (65*4 bytes with FP, 32*4 bytes without) + 3. Save RA (x1) on the stack at offset 28*4 + 4. Call _tx_thread_context_save to save thread context + 5. Call _tx_timer_interrupt to process the timer tick + 6. Call _tx_thread_context_restore to resume execution (does not return) + + Example (for CLINT timer): + + _tx_timer_interrupt_handler: + addi sp, sp, -32*4 + sw ra, 28*4(sp) + call _tx_thread_context_save + call _tx_timer_interrupt + j _tx_thread_context_restore + + The port assumes Machine mode (M-mode) execution. + For Supervisor mode (S-mode), use sstatus and SIE/SPIE instead of mstatus. + See the RISC-V Privileged Specification for more details. */
\ No newline at end of file diff --git a/ports/risc-v32/gnu/src/tx_thread_context_restore.S b/ports/risc-v32/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..73a07f61 --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,410 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_context_restore(VOID) +{ */ + .global _tx_thread_context_restore +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_exit // Call the ISR execution exit function +#endif + + /* Determine if interrupts are nested. */ + /* if (--_tx_thread_system_state) + { */ + + la t0, _tx_thread_system_state // Pickup addr of nested interrupt count + lw t1, 0(t0) // Pickup nested interrupt count + addi t1, t1, -1 // Decrement the nested interrupt counter + sw t1, 0(t0) // Store new nested count + beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + /* Recover floating point registers. */ +#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 + csrw fcsr, t0 // Restore fcsr +#endif + + /* Recover standard registers. */ + + /* Restore registers, + Skip global pointer because that does not change. + Also skip the saved registers since they have been restored by any function we called, + except s0 since we use it ourselves. */ + + lw t0, 30*4(sp) // Recover mepc + csrw mepc, t0 // Setup mepc + + /* Compose mstatus via read/modify/write to avoid clobbering unrelated bits. + Set MPIE and restore MPP to Machine, preserve other fields. */ + + csrr t1, mstatus + + /* Clear MPP/MPIE/MIE bits in t1 then set desired values. */ + + li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) + li t3, 0x1800 // Set MPP to Machine mode (bits 12:11) + + /* Construct new mstatus in t1: clear mask bits, set MPP/MPIE and optionally FP bit, + preserve everything except the bits we will modify. */ + + li t4, ~0x1888 // Clear mask for MPP/MPIE/MIE + and t1, t1, t4 + or t1, t1, t3 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t0, 0x2000 // Set FS bits (bits 14:13 to 01) for FP state + or t1, t1, t0 +#endif + csrw mstatus, t1 // Update mstatus safely + + lw ra, 28*4(sp) // Recover return address + lw t0, 19*4(sp) // Recover t0 + lw t1, 18*4(sp) // Recover t1 + lw t2, 17*4(sp) // Recover t2 + lw s0, 12*4(sp) // Recover s0 + lw a0, 27*4(sp) // Recover a0 + lw a1, 26*4(sp) // Recover a1 + lw a2, 25*4(sp) // Recover a2 + lw a3, 24*4(sp) // Recover a3 + lw a4, 23*4(sp) // Recover a4 + lw a5, 22*4(sp) // Recover a5 + lw a6, 21*4(sp) // Recover a6 + lw a7, 20*4(sp) // Recover a7 + lw t3, 16*4(sp) // Recover t3 + lw t4, 15*4(sp) // Recover t4 + 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 + mret // Return to point of interrupt + + /* } */ +_tx_thread_not_nested_restore: + /* Determine if a thread was interrupted and no preemption is required. */ + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + || (_tx_thread_preempt_disable)) + { */ + + la t0, _tx_thread_current_ptr // Pickup current thread pointer address + lw t1, 0(t0) // Pickup current thread pointer + + beqz t1, _tx_thread_idle_system_restore // If NULL, idle system 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 + + + la t0, _tx_thread_execute_ptr // Pickup thread execute pointer address + lw t2, 0(t0) // Pickup thread execute pointer + + bne t1, t2, _tx_thread_preempt_restore // If higher-priority thread is ready, preempt + + +_tx_thread_no_preempt_restore: + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + /* sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; */ + + lw sp, 8(t1) // Switch back to thread's stack + + /* Recover floating point registers. */ +#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 + csrw fcsr, t0 // Restore fcsr +#endif + + /* Recover the saved context and return to the point of interrupt. */ + + /* Recover standard registers. */ + /* Restore registers, + Skip global pointer because that does not change */ + + lw t0, 30*4(sp) // Recover mepc + csrw mepc, t0 // Setup mepc + + /* Compose mstatus via read/modify/write to avoid clobbering unrelated bits. */ + + csrr t1, mstatus + li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) + li t3, 0x1800 // Set MPP to Machine mode + li t4, ~0x1888 // Clear mask for MPP/MPIE/MIE + and t1, t1, t4 + or t1, t1, t3 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t0, 0x2000 // Set FS bits for FP state + or t1, t1, t0 +#endif + csrw mstatus, t1 // Update mstatus safely + + lw ra, 28*4(sp) // Recover return address + lw t0, 19*4(sp) // Recover t0 + lw t1, 18*4(sp) // Recover t1 + lw t2, 17*4(sp) // Recover t2 + lw s0, 12*4(sp) // Recover s0 + lw a0, 27*4(sp) // Recover a0 + lw a1, 26*4(sp) // Recover a1 + lw a2, 25*4(sp) // Recover a2 + lw a3, 24*4(sp) // Recover a3 + lw a4, 23*4(sp) // Recover a4 + lw a5, 22*4(sp) // Recover a5 + lw a6, 21*4(sp) // Recover a6 + lw a7, 20*4(sp) // Recover a7 + lw t3, 16*4(sp) // Recover t3 + lw t4, 15*4(sp) // Recover t4 + 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 + mret // Return to point of interrupt + + /* } + else + { */ +_tx_thread_preempt_restore: + /* Instead of directly activating the thread again, ensure we save the + entire stack frame by saving the remaining registers. */ + + lw t0, 8(t1) // Pickup thread's stack pointer + ori t3, zero, 1 // Build interrupt stack type + sw t3, 0(t0) // Store stack type + + /* Store floating point preserved registers. */ +#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 +#endif + + /* Store standard preserved registers. */ + + sw x9, 11*4(t0) // Store s1 + sw x18, 10*4(t0) // Store s2 + sw x19, 9*4(t0) // Store s3 + sw x20, 8*4(t0) // Store s4 + sw x21, 7*4(t0) // Store s5 + sw x22, 6*4(t0) // Store s6 + sw x23, 5*4(t0) // Store s7 + sw x24, 4*4(t0) // Store s8 + sw x25, 3*4(t0) // Store s9 + sw x26, 2*4(t0) // Store s10 + sw x27, 1*4(t0) // Store s11 + // Note: s0 is already stored! + + /* Save the remaining time-slice and disable it. */ + /* if (_tx_timer_time_slice) + { */ + + la t0, _tx_timer_time_slice // Pickup time slice variable address + lw t2, 0(t0) // Pickup time slice + beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing + + /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice + _tx_timer_time_slice = 0; */ + + sw t2, 24(t1) // Save current time slice + sw x0, 0(t0) // Clear global time slice + + + /* } */ +_tx_thread_dont_save_ts: + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + /* Return to the scheduler. */ + /* _tx_thread_schedule(); */ + + la t0, _tx_thread_current_ptr // Pickup current thread pointer address + sw x0, 0(t0) // Clear current thread pointer + + /* } */ + +_tx_thread_idle_system_restore: + /* Just return back to the scheduler! */ + j _tx_thread_schedule // Return to scheduler + +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_thread_context_save.S b/ports/risc-v32/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..66402934 --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_context_save.S @@ -0,0 +1,271 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_context_save(VOID) +{ */ + .global _tx_thread_context_save +_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) + sw ra, 28*4(sp) + */ + + sw t0, 19*4(sp) // Store t0 + sw t1, 18*4(sp) // Store t1 + + /* Check for a nested interrupt. */ + /* if (_tx_thread_system_state++) + { */ + + la t0, _tx_thread_system_state // Pickup addr of system state var + lw t1, 0(t0) // Pickup system state + addi t1, t1, 1 // Increment system state + sw t1, 0(t0) // Store system state + li t0, 1 + bgt t1, t0, _tx_thread_nested_save // If it's more than 1, nested interrupt + + /* First level interrupt, save the rest of the scratch registers and + check for a thread to preempt. */ + + sw t2, 17*4(sp) // Store t2 + sw s0, 12*4(sp) // Store s0 + sw a0, 27*4(sp) // Store a0 + sw a1, 26*4(sp) // Store a1 + sw a2, 25*4(sp) // Store a2 + sw a3, 24*4(sp) // Store a3 + sw a4, 23*4(sp) // Store a4 + sw a5, 22*4(sp) // Store a5 + sw a6, 21*4(sp) // Store a6 + sw a7, 20*4(sp) // Store a7 + sw t3, 16*4(sp) // Store t3 + sw t4, 15*4(sp) // Store t4 + sw t5, 14*4(sp) // Store t5 + sw t6, 13*4(sp) // Store t6 + + /* 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 + csrr t0, fcsr + sw t0, 63*4(sp) // Store fcsr +#endif + + csrr t0, mepc + sw t0, 30*4(sp) // Save it on the stack + + /* Save mstatus. */ + csrr t0, mstatus + sw t0, 29*4(sp) + + la t1, _tx_thread_current_ptr // Pickup address of current thread ptr + lw t2, 0(t1) // Pickup current thread pointer + beqz t2, _tx_thread_idle_system_save // If NULL, idle system was interrupted + + /* Save the current thread's stack pointer and switch to the system stack. */ + /* _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + sp = _tx_thread_system_stack_ptr; */ + + sw sp, 8(t2) // Save stack pointer + la t0, _tx_thread_system_stack_ptr + lw sp, 0(t0) // Switch to system stack + + /* Call the ISR execution exit function if enabled. */ +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + ret // Return to ISR + +_tx_thread_nested_save: + + /* Nested interrupt! Just save the scratch registers and return to the ISR. */ + + sw t2, 17*4(sp) // Store t2 + sw s0, 12*4(sp) // Store s0 + sw a0, 27*4(sp) // Store a0 + sw a1, 26*4(sp) // Store a1 + sw a2, 25*4(sp) // Store a2 + sw a3, 24*4(sp) // Store a3 + sw a4, 23*4(sp) // Store a4 + sw a5, 22*4(sp) // Store a5 + sw a6, 21*4(sp) // Store a6 + sw a7, 20*4(sp) // Store a7 + sw t3, 16*4(sp) // Store t3 + sw t4, 15*4(sp) // Store t4 + sw t5, 14*4(sp) // Store t5 + sw t6, 13*4(sp) // Store t6 + + /* 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 + csrr t0, fcsr + sw t0, 63*4(sp) // Store fcsr +#endif + + csrr t0, mepc + sw t0, 30*4(sp) // Save it on stack + + csrr t0, mstatus + sw t0, 29*4(sp) + + /* Call the ISR execution exit function if enabled. */ +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + ret // Return to ISR + +_tx_thread_idle_system_save: + + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + /* Interrupt occurred in the scheduling loop. */ + + /* } +} */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*4 // Recover stack frame - with floating point enabled +#else + addi sp, sp, 32*4 // Recover the reserved stack space +#endif + ret // Return to calling ISR diff --git a/ports/risc-v32/gnu/src/tx_thread_interrupt_control.S b/ports/risc-v32/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..aab2955b --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/**************************************************************************/ +/* UINT _tx_thread_interrupt_control(UINT new_posture) +{ */ + .global _tx_thread_interrupt_control +_tx_thread_interrupt_control: + + /* Pickup current interrupt posture. */ + + csrr a1, mstatus // Pickup mstatus + andi a1, a1, 0x08 // Mask out all but MIE + + /* Check for the new posture. */ + + beqz a0, _tx_thread_interrupt_disable // If 0, disable interrupts + + /* Enable interrupts. */ + + csrsi mstatus, 0x08 // Enable interrupts (MIE bit 3) + j _tx_thread_interrupt_control_exit // Return to caller + +_tx_thread_interrupt_disable: + + /* Disable interrupts. */ + + csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) + +_tx_thread_interrupt_control_exit: + + /* Return the old interrupt posture. */ + + mv a0, a1 // Setup return value + ret // Return to caller + +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_thread_schedule.S b/ports/risc-v32/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..1c235a2d --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_schedule.S @@ -0,0 +1,318 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_schedule(VOID) +{ */ + .global _tx_thread_schedule +_tx_thread_schedule: + + /* Enable interrupts. */ + + csrsi mstatus, 0x08 // Enable interrupts (MIE bit 3) + + /* Wait for a thread to execute. */ + /* do + { */ +_tx_thread_schedule_loop: + + la t0, _tx_thread_execute_ptr // Pickup address of execute ptr + lw t1, 0(t0) // Pickup execute pointer + bnez t1, _tx_thread_ready_to_run // If non-NULL, a thread is ready to run + +#ifndef TX_NO_WFI + wfi // Wait for interrupt +#endif + j _tx_thread_schedule_loop // Check again + + /* } + while (_tx_thread_execute_ptr == TX_NULL); */ + +_tx_thread_ready_to_run: + + /* At this point, t1 contains the pointer to the thread to execute. + Lockout interrupts. */ + + csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) + + /* Check _tx_thread_execute_ptr again, in case an interrupt occurred + between the check and the disable. */ + + lw t1, 0(t0) // Pickup execute pointer + beqz t1, _tx_thread_schedule_loop // If NULL, go back to wait loop + + /* Yes! We have a thread to execute. */ + /* _tx_thread_current_ptr = _tx_thread_execute_ptr; */ + + la t0, _tx_thread_current_ptr // Pickup address of current thread + sw t1, 0(t0) // Setup current thread pointer + + /* Increment the run count for this thread. */ + /* _tx_thread_current_ptr -> tx_thread_run_count++; */ + + lw t2, 4(t1) // Pickup run count + addi t2, t2, 1 // Increment run count + sw t2, 4(t1) // Store run count + + /* Setup time-slice values. */ + /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ + + lw t2, 24(t1) // Pickup thread time-slice + la t3, _tx_timer_time_slice // Pickup address of time-slice + sw t2, 0(t3) // Setup time-slice + + /* Call the thread execution enter function if enabled. */ +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + call _tx_execution_thread_enter // Call the thread execution enter function +#endif + + /* Switch to the thread's stack. */ + /* sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; */ + + lw sp, 8(t1) // Switch to thread stack + + /* Determine the type of stack frame. */ + /* if (*sp) + { */ + + lw t0, 0(sp) // Pickup stack type + beqz t0, _tx_thread_solicited_return // If 0, solicited return + + /* Recover floating point registers. */ +#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 + csrw fcsr, t0 // Restore fcsr +#endif + + /* Recover standard registers. */ + + lw t0, 30*4(sp) // Recover mepc + 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) + li t1, 0x2000 // Set FS bits for FP state + or t0, t0, t1 +#endif + csrw mstatus, t0 // Set mstatus + + lw ra, 28*4(sp) // Recover return address + lw t0, 19*4(sp) // Recover t0 + lw t1, 18*4(sp) // Recover t1 + lw t2, 17*4(sp) // Recover t2 + lw s0, 12*4(sp) // Recover s0 + lw x9, 11*4(sp) // Recover s1 + lw a0, 27*4(sp) // Recover a0 + lw a1, 26*4(sp) // Recover a1 + lw a2, 25*4(sp) // Recover a2 + lw a3, 24*4(sp) // Recover a3 + lw a4, 23*4(sp) // Recover a4 + lw a5, 22*4(sp) // Recover a5 + lw a6, 21*4(sp) // Recover a6 + lw a7, 20*4(sp) // Recover a7 + lw t3, 16*4(sp) // Recover t3 + lw t4, 15*4(sp) // Recover t4 + lw t5, 14*4(sp) // Recover t5 + lw t6, 13*4(sp) // Recover t6 + lw x18, 10*4(sp) // Recover s2 + lw x19, 9*4(sp) // Recover s3 + lw x20, 8*4(sp) // Recover s4 + lw x21, 7*4(sp) // Recover s5 + lw x22, 6*4(sp) // Recover s6 + lw x23, 5*4(sp) // Recover s7 + lw x24, 4*4(sp) // Recover s8 + lw x25, 3*4(sp) // Recover s9 + 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 + mret // Return to thread + +_tx_thread_solicited_return: + + /* Recover floating point registers. */ +#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 + csrw fcsr, t0 // Restore fcsr +#endif + + /* Recover standard registers. */ + + lw t0, 14*4(sp) // Recover mstatus + csrw mstatus, t0 // Restore mstatus + + lw ra, 13*4(sp) // Recover return address + lw s0, 12*4(sp) // Recover s0 + lw s1, 11*4(sp) // Recover s1 + lw x18, 10*4(sp) // Recover s2 + lw x19, 9*4(sp) // Recover s3 + lw x20, 8*4(sp) // Recover s4 + lw x21, 7*4(sp) // Recover s5 + lw x22, 6*4(sp) // Recover s6 + lw x23, 5*4(sp) // Recover s7 + lw x24, 4*4(sp) // Recover s8 + lw x25, 3*4(sp) // Recover s9 + 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 + 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 new file mode 100644 index 00000000..4ade60ca --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +{ */ + .global _tx_thread_stack_build +_tx_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the RISC-V should look like the following after it is built: + Reg Index + Stack Top: 1 0 Interrupt stack frame type + x27 1 Initial s11 + x26 2 Initial s10 + x25 3 Initial s9 + x24 4 Initial s8 + x23 5 Initial s7 + x22 6 Initial s6 + x21 7 Initial s5 + x20 8 Initial s4 + x19 9 Initial s3 + x18 10 Initial s2 + x9 11 Initial s1 + x8 12 Initial s0 + x31 13 Initial t6 + x30 14 Initial t5 + x29 15 Initial t4 + x28 16 Initial t3 + x7 17 Initial t2 + x6 18 Initial t1 + x5 19 Initial t0 + x17 20 Initial a7 + x16 21 Initial a6 + x15 22 Initial a5 + x14 23 Initial a4 + x13 24 Initial a3 + x12 25 Initial a2 + x11 26 Initial a1 + x10 27 Initial a0 + x1 28 Initial ra + -- 29 reserved + 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 + + Stack Bottom: (higher memory address) */ + + lw t0, 16(a0) // Pickup end of stack area + li t1, ~15 // Build 16-byte alignment mask + and t0, t0, t1 // Make sure 16-byte alignment + + /* 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 + li t1, 1 // Build stack type + sw t1, 0*4(t0) // Place stack type on the top + sw zero, 1*4(t0) // Initial s11 + sw zero, 2*4(t0) // Initial s10 + sw zero, 3*4(t0) // Initial s9 + sw zero, 4*4(t0) // Initial s8 + sw zero, 5*4(t0) // Initial s7 + sw zero, 6*4(t0) // Initial s6 + sw zero, 7*4(t0) // Initial s5 + sw zero, 8*4(t0) // Initial s4 + sw zero, 9*4(t0) // Initial s3 + sw zero, 10*4(t0) // Initial s2 + sw zero, 11*4(t0) // Initial s1 + sw zero, 12*4(t0) // Initial s0 + sw zero, 13*4(t0) // Initial t6 + sw zero, 14*4(t0) // Initial t5 + sw zero, 15*4(t0) // Initial t4 + sw zero, 16*4(t0) // Initial t3 + sw zero, 17*4(t0) // Initial t2 + sw zero, 18*4(t0) // Initial t1 + sw zero, 19*4(t0) // Initial t0 + sw zero, 20*4(t0) // Initial a7 + sw zero, 21*4(t0) // Initial a6 + sw zero, 22*4(t0) // Initial a5 + sw zero, 23*4(t0) // Initial a4 + sw zero, 24*4(t0) // Initial a3 + sw zero, 25*4(t0) // Initial a2 + sw zero, 26*4(t0) // Initial a1 + 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) +#else + sw zero, 31*4(t0) // Reserved word (0) +#endif + + /* Setup stack pointer. */ + /* thread_ptr -> tx_thread_stack_ptr = t0; */ + + sw t0, 8(a0) // Save stack pointer in thread's + ret // control block and return +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_thread_system_return.S b/ports/risc-v32/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..4090e7b2 --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_system_return.S @@ -0,0 +1,168 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the system. Only a minimal context */ +/* is saved since the compiler assumes temp registers are going to get */ +/* slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_system_return(VOID) +{ */ + .global _tx_thread_system_return +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + /* sp -= sizeof(stack_frame); */ + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -29*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 + + /* Store floating point preserved registers. */ +#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 + 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 zero, 0(sp) // Solicited stack type + sw ra, 13*4(sp) // Save return address + sw s0, 12*4(sp) // Save s0 + sw s1, 11*4(sp) // Save s1 + sw s2, 10*4(sp) // Save s2 + sw s3, 9*4(sp) // Save s3 + sw s4, 8*4(sp) // Save s4 + sw s5, 7*4(sp) // Save s5 + sw s6, 6*4(sp) // Save s6 + sw s7, 5*4(sp) // Save s7 + sw s8, 4*4(sp) // Save s8 + sw s9, 3*4(sp) // Save s9 + sw s10, 2*4(sp) // Save s10 + sw s11, 1*4(sp) // Save s11 + csrr t0, mstatus // Pickup mstatus + sw t0, 14*4(sp) // Save mstatus + + + /* Lockout interrupts. will be enabled in _tx_thread_schedule */ + + csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + call _tx_execution_thread_exit // Call the thread execution exit function +#endif + + la t0, _tx_thread_current_ptr // Pickup address of pointer + lw t1, 0(t0) // Pickup current thread pointer + la t2, _tx_thread_system_stack_ptr // Pickup stack pointer address + + /* Save current stack and switch to system stack. */ + /* _tx_thread_current_ptr -> tx_thread_stack_ptr = SP; + SP = _tx_thread_system_stack_ptr; */ + + sw sp, 8(t1) // Save stack pointer + lw sp, 0(t2) // Switch to system stack + + /* Determine if the time-slice is active. */ + /* if (_tx_timer_time_slice) + { */ + + la t4, _tx_timer_time_slice // Pickup time slice variable addr + lw t3, 0(t4) // Pickup time slice value + la t2, _tx_thread_schedule // Pickup address of scheduling loop + beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it + + /* Save time-slice for the thread and clear the current time-slice. */ + /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + _tx_timer_time_slice = 0; */ + + sw t3, 24(t1) // Save current time-slice for thread + sw zero, 0(t4) // Clear time-slice variable + + /* } */ +_tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + sw x0, 0(t0) // Clear current thread pointer + jr t2 // Return to thread scheduler + +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_timer_interrupt.S b/ports/risc-v32/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..d3b2fc60 --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,204 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .section .text + .align 4 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt RISC-V32/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/**************************************************************************/ +/* VOID _tx_timer_interrupt(VOID) +{ */ + .global _tx_timer_interrupt +_tx_timer_interrupt: + + /* Increment the system clock. */ + /* _tx_timer_system_clock++; */ + + la t0, _tx_timer_system_clock // Pickup address of system clock + lw t1, 0(t0) // Pickup system clock + la t2, _tx_timer_time_slice // Pickup address of time slice + lw t3, 0(t2) // Pickup time slice + addi t1, t1, 1 // Increment system clock + sw t1, 0(t0) // Store new system clock + li t6, 0 // Clear local expired flag + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + beqz t3, _tx_timer_no_time_slice // If 0, skip time slice processing + addi t3, t3, -1 // Decrement the time slice + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + sw t3, 0(t2) // Store new time slice + + /* Check for expiration. */ + /* if (_tx_timer_time_slice == 0) */ + + bgtz 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. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + la t4, _tx_timer_expired_time_slice // Get address of expired flag + sw t1, 0(t4) // Set expired flag (UINT) + ori t6, t6, 1 // Set local expired flag + + /* } */ + +_tx_timer_no_time_slice: + + /* Test for timer expiration. */ + /* if (*_tx_timer_current_ptr) + { */ + + la t0, _tx_timer_current_ptr // Pickup address of current ptr + lw t1, 0(t0) // Pickup current pointer (word) + lw t3, 0(t1) // Pickup the current timer entry (word) + la t2, _tx_timer_expired // Pickup address of timer expired flag + li t4, 1 // Build TX_TRUE flag + beqz t3, _tx_timer_no_timer // If NULL, no timer has expired + + /* Set expiration flag. */ + /* _tx_timer_expired = TX_TRUE; */ + + ori t6, t6, 2 // Set local expired flag + sw t4, 0(t2) // Set expired flag in memory (UINT) + j _tx_timer_done // Finished timer processing + + + /* } + else + { */ +_tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + /* _tx_timer_current_ptr++; */ + + /* Check for wrap-around. */ + /* if (_tx_timer_current_ptr == _tx_timer_list_end) */ + + la t2, _tx_timer_list_end // Pickup address of list end pointer + lw t3, 0(t2) // Pickup actual list end + addi t1, t1, 4 // Point to next timer entry + sw t1, 0(t0) // Store new timer pointer + bne t1, t3, _tx_timer_skip_wrap // If not same, good pointer + + /* Wrap to beginning of list. */ + /* _tx_timer_current_ptr = _tx_timer_list_start; */ + + la t2, _tx_timer_list_start // Pickup address of list start pointer + lw t4, 0(t2) // Pickup start of the list + sw t4, 0(t0) // Store new timer pointer + + +_tx_timer_skip_wrap: + /* } */ + +_tx_timer_done: + + + /* See if anything has expired. */ + /* if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + { */ + + beqz t6, _tx_timer_nothing_expired // If nothing expired skip the rest + addi sp, sp, -16 // Allocate some storage on the stack + sw t6, 0(sp) // Save local expired flag + sw ra, 4(sp) // Save ra + + /* Did a timer expire? */ + /* if (_tx_timer_expired) + { */ + + andi t2, t6, 2 // Isolate the timer expired bit + beqz t2, _tx_timer_dont_activate // No, timer not expired + + /* Call the timer expiration processing. */ + /* _tx_timer_expiration_process(void); */ + + call _tx_timer_expiration_process // Call _tx_timer_expiration_process + lw t6, 0(sp) // Recover local expired flag + + /* } */ +_tx_timer_dont_activate: + + /* Did time slice expire? */ + /* if (_tx_timer_expired_time_slice) + { */ + + andi t2, t6, 1 // Is the timer expired bit set? + beqz t2, _tx_timer_not_ts_expiration // If not, skip time slice processing + + /* Time slice interrupted thread. */ + /* _tx_thread_time_slice(); */ + + call _tx_thread_time_slice // Call time slice + + /* } */ + +_tx_timer_not_ts_expiration: + + lw ra, 4(sp) // Recover ra + addi sp, sp, 16 // Recover stack space + /* } */ + +_tx_timer_nothing_expired: + + ret + +/* } */
\ No newline at end of file |
