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