1 /*
2 * FreeRTOS Kernel V10.6.2
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
26 *
27 */
28
29 /* Standard includes. */
30 #include <stdlib.h>
31
32 /* Scheduler includes. */
33 #include "FreeRTOS.h"
34 #include "task.h"
35
36 #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
37 /* Check the configuration. */
38 #if( configMAX_PRIORITIES > 32 )
39 #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
40 #endif
41 #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
42
43 #ifndef configSETUP_TICK_INTERRUPT
44 #error configSETUP_TICK_INTERRUPT() must be defined in FreeRTOSConfig.h to call the function that sets up the tick interrupt.
45 #endif
46
47 #ifndef configCLEAR_TICK_INTERRUPT
48 #error configCLEAR_TICK_INTERRUPT must be defined in FreeRTOSConfig.h to clear which ever interrupt was used to generate the tick interrupt.
49 #endif
50
51 /* A critical section is exited when the critical section nesting count reaches
52 this value. */
53 #define portNO_CRITICAL_NESTING ( ( uint32_t ) 0 )
54
55 /* Tasks are not created with a floating point context, but can be given a
56 floating point context after they have been created. A variable is stored as
57 part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task
58 does not have an FPU context, or any other value if the task does have an FPU
59 context. */
60 #define portNO_FLOATING_POINT_CONTEXT ( ( StackType_t ) 0 )
61
62 /* Constants required to setup the initial task context. */
63 #define portINITIAL_SPSR ( ( StackType_t ) 0x1f ) /* System mode, ARM mode, IRQ enabled FIQ enabled. */
64 #define portTHUMB_MODE_BIT ( ( StackType_t ) 0x20 )
65 #define portTHUMB_MODE_ADDRESS ( 0x01UL )
66
67 /* Masks all bits in the APSR other than the mode bits. */
68 #define portAPSR_MODE_BITS_MASK ( 0x1F )
69
70 /* The value of the mode bits in the APSR when the CPU is executing in user
71 mode. */
72 #define portAPSR_USER_MODE ( 0x10 )
73
74 /* Let the user override the pre-loading of the initial LR with the address of
75 prvTaskExitError() in case it messes up unwinding of the stack in the
76 debugger. */
77 #ifdef configTASK_RETURN_ADDRESS
78 #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
79 #else
80 #define portTASK_RETURN_ADDRESS prvTaskExitError
81 #endif
82
83 /*-----------------------------------------------------------*/
84
85 /*
86 * Starts the first task executing. This function is necessarily written in
87 * assembly code so is implemented in portASM.s.
88 */
89 extern void vPortRestoreTaskContext( void );
90
91 /*
92 * Used to catch tasks that attempt to return from their implementing function.
93 */
94 static void prvTaskExitError( void );
95
96 /*-----------------------------------------------------------*/
97
98 /* A variable is used to keep track of the critical section nesting. This
99 variable has to be stored as part of the task context and must be initialised to
100 a non zero value to ensure interrupts don't inadvertently become unmasked before
101 the scheduler starts. As it is stored as part of the task context it will
102 automatically be set to 0 when the first task is started. */
103 volatile uint32_t ulCriticalNesting = 9999UL;
104
105 /* Saved as part of the task context. If ulPortTaskHasFPUContext is non-zero then
106 a floating point context must be saved and restored for the task. */
107 volatile uint32_t ulPortTaskHasFPUContext = pdFALSE;
108
109 /* Set to 1 to pend a context switch from an ISR. */
110 volatile uint32_t ulPortYieldRequired = pdFALSE;
111
112 /* Counts the interrupt nesting depth. A context switch is only performed if
113 if the nesting depth is 0. */
114 volatile uint32_t ulPortInterruptNesting = 0UL;
115
116 /* Used in the asm file to clear an interrupt. */
117 __attribute__(( used )) const uint32_t ulICCEOIR = configEOI_ADDRESS;
118
119 /*-----------------------------------------------------------*/
120
121 /*
122 * See header file for description.
123 */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)124 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
125 {
126 /* Setup the initial stack of the task. The stack is set exactly as
127 expected by the portRESTORE_CONTEXT() macro.
128
129 The fist real value on the stack is the status register, which is set for
130 system mode, with interrupts enabled. A few NULLs are added first to ensure
131 GDB does not try decoding a non-existent return address. */
132 *pxTopOfStack = ( StackType_t ) NULL;
133 pxTopOfStack--;
134 *pxTopOfStack = ( StackType_t ) NULL;
135 pxTopOfStack--;
136 *pxTopOfStack = ( StackType_t ) NULL;
137 pxTopOfStack--;
138 *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;
139
140 if( ( ( uint32_t ) pxCode & portTHUMB_MODE_ADDRESS ) != 0x00UL )
141 {
142 /* The task will start in THUMB mode. */
143 *pxTopOfStack |= portTHUMB_MODE_BIT;
144 }
145
146 pxTopOfStack--;
147
148 /* Next the return address, which in this case is the start of the task. */
149 *pxTopOfStack = ( StackType_t ) pxCode;
150 pxTopOfStack--;
151
152 /* Next all the registers other than the stack pointer. */
153 *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* R14 */
154 pxTopOfStack--;
155 *pxTopOfStack = ( StackType_t ) 0x12121212; /* R12 */
156 pxTopOfStack--;
157 *pxTopOfStack = ( StackType_t ) 0x11111111; /* R11 */
158 pxTopOfStack--;
159 *pxTopOfStack = ( StackType_t ) 0x10101010; /* R10 */
160 pxTopOfStack--;
161 *pxTopOfStack = ( StackType_t ) 0x09090909; /* R9 */
162 pxTopOfStack--;
163 *pxTopOfStack = ( StackType_t ) 0x08080808; /* R8 */
164 pxTopOfStack--;
165 *pxTopOfStack = ( StackType_t ) 0x07070707; /* R7 */
166 pxTopOfStack--;
167 *pxTopOfStack = ( StackType_t ) 0x06060606; /* R6 */
168 pxTopOfStack--;
169 *pxTopOfStack = ( StackType_t ) 0x05050505; /* R5 */
170 pxTopOfStack--;
171 *pxTopOfStack = ( StackType_t ) 0x04040404; /* R4 */
172 pxTopOfStack--;
173 *pxTopOfStack = ( StackType_t ) 0x03030303; /* R3 */
174 pxTopOfStack--;
175 *pxTopOfStack = ( StackType_t ) 0x02020202; /* R2 */
176 pxTopOfStack--;
177 *pxTopOfStack = ( StackType_t ) 0x01010101; /* R1 */
178 pxTopOfStack--;
179 *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
180 pxTopOfStack--;
181
182 /* The task will start with a critical nesting count of 0 as interrupts are
183 enabled. */
184 *pxTopOfStack = portNO_CRITICAL_NESTING;
185 pxTopOfStack--;
186
187 /* The task will start without a floating point context. A task that uses
188 the floating point hardware must call vPortTaskUsesFPU() before executing
189 any floating point instructions. */
190 *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
191
192 return pxTopOfStack;
193 }
194 /*-----------------------------------------------------------*/
195
prvTaskExitError(void)196 static void prvTaskExitError( void )
197 {
198 /* A function that implements a task must not exit or attempt to return to
199 its caller as there is nothing to return to. If a task wants to exit it
200 should instead call vTaskDelete( NULL ).
201
202 Artificially force an assert() to be triggered if configASSERT() is
203 defined, then stop here so application writers can catch the error. */
204 configASSERT( ulPortInterruptNesting == ~0UL );
205 portDISABLE_INTERRUPTS();
206 for( ;; );
207 }
208 /*-----------------------------------------------------------*/
209
xPortStartScheduler(void)210 BaseType_t xPortStartScheduler( void )
211 {
212 uint32_t ulAPSR;
213
214 /* Only continue if the CPU is not in User mode. The CPU must be in a
215 Privileged mode for the scheduler to start. */
216 __asm volatile ( "MRS %0, APSR" : "=r" ( ulAPSR ) :: "memory" );
217 ulAPSR &= portAPSR_MODE_BITS_MASK;
218 configASSERT( ulAPSR != portAPSR_USER_MODE );
219
220 if( ulAPSR != portAPSR_USER_MODE )
221 {
222 /* Start the timer that generates the tick ISR. */
223 portDISABLE_INTERRUPTS();
224 configSETUP_TICK_INTERRUPT();
225
226 /* Start the first task executing. */
227 vPortRestoreTaskContext();
228 }
229
230 /* Will only get here if vTaskStartScheduler() was called with the CPU in
231 a non-privileged mode or the binary point register was not set to its lowest
232 possible value. prvTaskExitError() is referenced to prevent a compiler
233 warning about it being defined but not referenced in the case that the user
234 defines their own exit address. */
235 ( void ) prvTaskExitError;
236 return 0;
237 }
238 /*-----------------------------------------------------------*/
239
vPortEndScheduler(void)240 void vPortEndScheduler( void )
241 {
242 /* Not implemented in ports where there is nothing to return to.
243 Artificially force an assert. */
244 configASSERT( ulCriticalNesting == 1000UL );
245 }
246 /*-----------------------------------------------------------*/
247
vPortEnterCritical(void)248 void vPortEnterCritical( void )
249 {
250 portDISABLE_INTERRUPTS();
251
252 /* Now interrupts are disabled ulCriticalNesting can be accessed
253 directly. Increment ulCriticalNesting to keep a count of how many times
254 portENTER_CRITICAL() has been called. */
255 ulCriticalNesting++;
256
257 /* This is not the interrupt safe version of the enter critical function so
258 assert() if it is being called from an interrupt context. Only API
259 functions that end in "FromISR" can be used in an interrupt. Only assert if
260 the critical nesting count is 1 to protect against recursive calls if the
261 assert function also uses a critical section. */
262 if( ulCriticalNesting == 1 )
263 {
264 configASSERT( ulPortInterruptNesting == 0 );
265 }
266 }
267 /*-----------------------------------------------------------*/
268
vPortExitCritical(void)269 void vPortExitCritical( void )
270 {
271 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
272 {
273 /* Decrement the nesting count as the critical section is being
274 exited. */
275 ulCriticalNesting--;
276
277 /* If the nesting level has reached zero then all interrupt
278 priorities must be re-enabled. */
279 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
280 {
281 /* Critical nesting has reached zero so all interrupt priorities
282 should be unmasked. */
283 portENABLE_INTERRUPTS();
284 }
285 }
286 }
287 /*-----------------------------------------------------------*/
288
FreeRTOS_Tick_Handler(void)289 void FreeRTOS_Tick_Handler( void )
290 {
291 uint32_t ulInterruptStatus;
292
293 ulInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
294
295 /* Increment the RTOS tick. */
296 if( xTaskIncrementTick() != pdFALSE )
297 {
298 ulPortYieldRequired = pdTRUE;
299 }
300
301 portCLEAR_INTERRUPT_MASK_FROM_ISR( ulInterruptStatus );
302
303 configCLEAR_TICK_INTERRUPT();
304 }
305 /*-----------------------------------------------------------*/
306
vPortTaskUsesFPU(void)307 void vPortTaskUsesFPU( void )
308 {
309 uint32_t ulInitialFPSCR = 0;
310
311 /* A task is registering the fact that it needs an FPU context. Set the
312 FPU flag (which is saved as part of the task context). */
313 ulPortTaskHasFPUContext = pdTRUE;
314
315 /* Initialise the floating point status register. */
316 __asm volatile ( "FMXR FPSCR, %0" :: "r" (ulInitialFPSCR) : "memory" );
317 }
318 /*-----------------------------------------------------------*/
319