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