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