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