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