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