1/*************************************************************************** 2 * Copyright (c) 2024 Microsoft Corporation 3 * 4 * This program and the accompanying materials are made available under the 5 * terms of the MIT License which is available at 6 * https://opensource.org/licenses/MIT. 7 * 8 * SPDX-License-Identifier: MIT 9 **************************************************************************/ 10 11 12/**************************************************************************/ 13/**************************************************************************/ 14/** */ 15/** ThreadX Component */ 16/** */ 17/** Thread */ 18/** */ 19/**************************************************************************/ 20/**************************************************************************/ 21 22 .text 23 .align 4 24/**************************************************************************/ 25/* */ 26/* FUNCTION RELEASE */ 27/* */ 28/* _tx_thread_schedule Cortex-Mx/GHS */ 29/* 6.1.7 */ 30/* AUTHOR */ 31/* */ 32/* Scott Larson, Microsoft Corporation */ 33/* */ 34/* DESCRIPTION */ 35/* */ 36/* This function waits for a thread control block pointer to appear in */ 37/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ 38/* in the variable, the corresponding thread is resumed. */ 39/* */ 40/* INPUT */ 41/* */ 42/* None */ 43/* */ 44/* OUTPUT */ 45/* */ 46/* None */ 47/* */ 48/* CALLS */ 49/* */ 50/* None */ 51/* */ 52/* CALLED BY */ 53/* */ 54/* _tx_initialize_kernel_enter ThreadX entry function */ 55/* _tx_thread_system_return Return to system from thread */ 56/* _tx_thread_context_restore Restore thread's context */ 57/* */ 58/* RELEASE HISTORY */ 59/* */ 60/* DATE NAME DESCRIPTION */ 61/* */ 62/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ 63/* */ 64/**************************************************************************/ 65// VOID _tx_thread_schedule(VOID) 66// { 67 .globl _tx_thread_schedule 68_tx_thread_schedule: 69 70 /* This function should only ever be called on Cortex-M 71 from the first schedule request. Subsequent scheduling occurs 72 from the PendSV handling routine below. */ 73 74 /* Clear the preempt-disable flag to enable rescheduling after initialization on Cortex-M targets. */ 75 76 MOV r0, #0 // Build value for TX_FALSE 77 LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag 78 STR r0, [r2, #0] // Clear preempt disable flag 79 80 /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ 81 82#ifdef __VFP__ 83 MRS r0, CONTROL // Pickup current CONTROL register 84 BIC r0, r0, #4 // Clear the FPCA bit 85 MSR CONTROL, r0 // Setup new CONTROL register 86#endif 87 88 /* Enable interrupts */ 89 90 CPSIE i 91 92 /* Enter the scheduler for the first time. */ 93 94 MOV r0, #0x10000000 // Load PENDSVSET bit 95 MOV r1, #0xE000E000 // Load NVIC base 96 STR r0, [r1, #0xD04] // Set PENDSVBIT in ICSR 97 DSB // Complete all memory accesses 98 ISB // Flush pipeline 99 100 /* Wait here for the PendSV to take place. */ 101 102__tx_wait_here: 103 B __tx_wait_here // Wait for the PendSV to happen 104 105 .type _tx_thread_schedule,$function 106 .size _tx_thread_schedule,.-_tx_thread_schedule 107// } 108 109 /* Generic context switching PendSV handler. */ 110 111 .globl PendSV_Handler 112 .globl __tx_PendSVHandler 113PendSV_Handler: 114__tx_PendSVHandler: 115 116 /* Get current thread value and new thread pointer. */ 117 118__tx_ts_handler: 119 120#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY 121 /* Call the thread exit function to indicate the thread is no longer executing. */ 122 CPSID i // Disable interrupts 123 PUSH {r0, lr} // Save LR (and r0 just for alignment) 124 BL _tx_execution_thread_exit // Call the thread exit function 125 POP {r0, lr} // Recover LR 126 CPSIE i // Enable interrupts 127#endif 128 129 LDR r0, =_tx_thread_current_ptr // Build current thread pointer address 130 LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address 131 MOV r3, #0 // Build NULL value 132 LDR r1, [r0] // Pickup current thread pointer 133 134 /* Determine if there is a current thread to finish preserving. */ 135 136 CBZ r1, __tx_ts_new // If NULL, skip preservation 137 138 /* Recover PSP and preserve current thread context. */ 139 140 STR r3, [r0] // Set _tx_thread_current_ptr to NULL 141 MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) 142 STMDB r12!, {r4-r11} // Save its remaining registers 143#ifdef __VFP__ 144 TST LR, #0x10 // Determine if the VFP extended frame is present 145 BNE _skip_vfp_save 146 VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers 147_skip_vfp_save: 148#endif 149 LDR r4, =_tx_timer_time_slice // Build address of time-slice variable 150 STR.W LR, [r12, #-0x4]! // Save LR on the stack 151 152 /* Determine if time-slice is active. If it isn't, skip time handling processing. */ 153 154 LDR r5, [r4] // Pickup current time-slice 155 STR r12, [r1, #8] // Save the thread stack pointer 156 CBZ r5, __tx_ts_new // If not active, skip processing 157 158 /* Time-slice is active, save the current thread's time-slice and clear the global time-slice variable. */ 159 160 STR r5, [r1, #24] // Save current time-slice 161 162 /* Clear the global time-slice. */ 163 164 STR r3, [r4] // Clear time-slice 165 166 /* Executing thread is now completely preserved!!! */ 167 168__tx_ts_new: 169 170 /* Now we are looking for a new thread to execute! */ 171 172 CPSID i // Disable interrupts 173 LDR r1, [r2] // Is there another thread ready to execute? 174 CBZ r1, __tx_ts_wait // No, skip to the wait processing 175 176 /* Yes, another thread is ready for else, make the current thread the new thread. */ 177 178 STR r1, [r0] // Setup the current thread pointer to the new thread 179 CPSIE i // Enable interrupts 180 181 /* Increment the thread run count. */ 182 183__tx_ts_restore: 184 LDR r7, [r1, #4] // Pickup the current thread run count 185 LDR r4, =_tx_timer_time_slice // Build address of time-slice variable 186 LDR r5, [r1, #24] // Pickup thread's current time-slice 187 ADD r7, r7, #1 // Increment the thread run count 188 STR r7, [r1, #4] // Store the new run count 189 190 /* Setup global time-slice with thread's current time-slice. */ 191 192 STR r5, [r4] // Setup global time-slice 193 194#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY 195 /* Call the thread entry function to indicate the thread is executing. */ 196 PUSH {r0, r1} // Save r0 and r1 197 BL _tx_execution_thread_enter // Call the thread execution enter function 198 POP {r0, r1} // Recover r0 and r1 199#endif 200 201 /* Restore the thread context and PSP. */ 202 203 LDR r12, [r1, #8] // Pickup thread's stack pointer 204 LDR.W LR, [r12], #4 // Pickup LR 205#ifdef __VFP__ 206 TST LR, #0x10 // Determine if the VFP extended frame is present 207 BNE _skip_vfp_restore // If not, skip VFP restore 208 VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers 209_skip_vfp_restore: 210#endif 211 LDMIA r12!, {r4-r11} // Recover thread's registers 212 MSR PSP, r12 // Setup the thread's stack pointer 213 214 /* Return to thread. */ 215 216 BX lr // Return to thread! 217 218 /* The following is the idle wait processing... in this case, no threads are ready for execution and the 219 system will simply be idle until an interrupt occurs that makes a thread ready. Note that interrupts 220 are disabled to allow use of WFI for waiting for a thread to arrive. */ 221 222__tx_ts_wait: 223 CPSID i // Disable interrupts 224 LDR r1, [r2] // Pickup the next thread to execute pointer 225 STR r1, [r0] // Store it in the current pointer 226 CBNZ r1, __tx_ts_ready // If non-NULL, a new thread is ready! 227 228#ifdef TX_LOW_POWER 229 PUSH {r0-r3} 230 BL tx_low_power_enter // Possibly enter low power mode 231 POP {r0-r3} 232#endif 233 234#ifdef TX_ENABLE_WFI 235 DSB // Ensure no outstanding memory transactions 236 WFI // Wait for interrupt 237 ISB // Ensure pipeline is flushed 238#endif 239 240#ifdef TX_LOW_POWER 241 PUSH {r0-r3} 242 BL tx_low_power_exit // Exit low power mode 243 POP {r0-r3} 244#endif 245 246 CPSIE i // Enable interrupts 247 B __tx_ts_wait // Loop to continue waiting 248 249 /* At this point, we have a new thread ready to go. Clear any newly pended PendSV - since we are 250 already in the handler! */ 251 252__tx_ts_ready: 253 MOV r7, #0x08000000 // Build clear PendSV value 254 MOV r8, #0xE000E000 // Build base NVIC address 255 STR r7, [r8, #0xD04] // Clear any PendSV 256 257 /* Re-enable interrupts and restore new thread. */ 258 259 CPSIE i // Enable interrupts 260 B __tx_ts_restore // Restore the thread 261// } 262 263 .type __tx_PendSVHandler,$function 264 .size __tx_PendSVHandler,.-__tx_PendSVHandler 265 266#ifdef __VFP__ 267 268 .globl tx_thread_fpu_enable 269tx_thread_fpu_enable: 270 271 /* Automatic VPF logic is supported, this function is present only for 272 backward compatibility purposes and therefore simply returns. */ 273 274 BX LR // Return to caller 275 276 .type tx_thread_fpu_enable,$function 277 .size tx_thread_fpu_enable,.-tx_thread_fpu_enable 278 279 .global tx_thread_fpu_disable 280tx_thread_fpu_disable: 281 282 /* Automatic VPF logic is supported, this function is present only for 283 backward compatibility purposes and therefore simply returns. */ 284 285 BX LR // Return to caller 286 287 .type tx_thread_fpu_disable,$function 288 .size tx_thread_fpu_disable,.-tx_thread_fpu_disable 289 290#endif 291