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