summaryrefslogtreecommitdiff
path: root/ports/risc-v32/gnu/src/tx_thread_context_restore.S
diff options
context:
space:
mode:
Diffstat (limited to 'ports/risc-v32/gnu/src/tx_thread_context_restore.S')
-rw-r--r--ports/risc-v32/gnu/src/tx_thread_context_restore.S416
1 files changed, 416 insertions, 0 deletions
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..ba553a46
--- /dev/null
+++ b/ports/risc-v32/gnu/src/tx_thread_context_restore.S
@@ -0,0 +1,416 @@
+/***************************************************************************
+ * 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 */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */
+/* */
+/**************************************************************************/
+/* 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
+
+/* } */