1 /*
2 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
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 #include <string.h>
32
33 /* Scheduler includes. */
34 #include "FreeRTOS.h"
35 #include "task.h"
36
37 #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
38 /* Check the configuration. */
39 #if ( configMAX_PRIORITIES > 32 )
40 #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.
41 #endif
42 #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
43
44 #ifndef configSETUP_TICK_INTERRUPT
45 #error configSETUP_TICK_INTERRUPT() must be defined in FreeRTOSConfig.h to call the function that sets up the tick interrupt.
46 #endif
47
48 #ifndef configCLEAR_TICK_INTERRUPT
49 #error configCLEAR_TICK_INTERRUPT must be defined in FreeRTOSConfig.h to clear which ever interrupt was used to generate the tick interrupt.
50 #endif
51
52 /* A critical section is exited when the critical section nesting count reaches
53 * this value. */
54 #define portNO_CRITICAL_NESTING ( ( uint32_t ) 0 )
55
56 /* Tasks are not created with a floating point context, but can be given a
57 * floating point context after they have been created. A variable is stored as
58 * part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task
59 * does not have an FPU context, or any other value if the task does have an FPU
60 * context. */
61 #define portNO_FLOATING_POINT_CONTEXT ( ( StackType_t ) 0 )
62
63 /* Constants required to setup the initial task context. */
64 #define portINITIAL_SPSR ( ( StackType_t ) 0x1f ) /* System mode, ARM mode, IRQ enabled FIQ enabled. */
65 #define portTHUMB_MODE_BIT ( ( StackType_t ) 0x20 )
66 #define portTHUMB_MODE_ADDRESS ( 0x01UL )
67
68 /* Masks all bits in the APSR other than the mode bits. */
69 #define portAPSR_MODE_BITS_MASK ( 0x1F )
70
71 /* The value of the mode bits in the APSR when the CPU is executing in user
72 * mode. */
73 #define portAPSR_USER_MODE ( 0x10 )
74
75 /* Let the user override the pre-loading of the initial LR with the address of
76 * prvTaskExitError() in case it messes up unwinding of the stack in the
77 * debugger. */
78 #ifdef configTASK_RETURN_ADDRESS
79 #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
80 #else
81 #define portTASK_RETURN_ADDRESS prvTaskExitError
82 #endif
83
84 /* The space on the stack required to hold the FPU registers. */
85 #if ( configFPU_D32 == 1 )
86 #define portFPU_REGISTER_WORDS ( ( 32 * 2 ) + 1 ) /* D0-D31 and FPSCR. */
87 #else
88 #define portFPU_REGISTER_WORDS ( ( 16 * 2 ) + 1 ) /* D0-D15 and FPSCR. */
89 #endif /* configFPU_D32 */
90
91 /*-----------------------------------------------------------*/
92
93 /*
94 * These functions are necessarily written in assembly code, so are implemented
95 * in portASM.S.
96 */
97 extern void vPortRestoreTaskContext( void );
98 extern void vPortInitialiseFPSCR( void );
99 extern uint32_t ulReadAPSR( void );
100
101 /*
102 * Used to catch tasks that attempt to return from their implementing function.
103 */
104 static void prvTaskExitError( void );
105
106 /*-----------------------------------------------------------*/
107
108 /* A variable is used to keep track of the critical section nesting. This
109 * variable has to be stored as part of the task context and must be initialised to
110 * a non zero value to ensure interrupts don't inadvertently become unmasked before
111 * the scheduler starts. As it is stored as part of the task context it will
112 * automatically be set to 0 when the first task is started. */
113 volatile uint32_t ulCriticalNesting = 9999UL;
114
115 /* Saved as part of the task context. If ulPortTaskHasFPUContext is non-zero then
116 * a floating point context must be saved and restored for the task. */
117 volatile uint32_t ulPortTaskHasFPUContext = pdFALSE;
118
119 /* Set to 1 to pend a context switch from an ISR. */
120 volatile uint32_t ulPortYieldRequired = pdFALSE;
121
122 /* Counts the interrupt nesting depth. A context switch is only performed if
123 * if the nesting depth is 0. */
124 volatile uint32_t ulPortInterruptNesting = 0UL;
125
126 /* Used in the asm file to clear an interrupt. */
127 __attribute__( ( used ) ) const uint32_t ulICCEOIR = configEOI_ADDRESS;
128
129 /*-----------------------------------------------------------*/
130
131 /*
132 * See header file for description.
133 */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)134 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
135 TaskFunction_t pxCode,
136 void * pvParameters )
137 {
138 /* Setup the initial stack of the task. The stack is set exactly as
139 * expected by the portRESTORE_CONTEXT() macro.
140 *
141 * The fist real value on the stack is the status register, which is set for
142 * system mode, with interrupts enabled. A few NULLs are added first to ensure
143 * GDB does not try decoding a non-existent return address. */
144 *pxTopOfStack = ( StackType_t ) NULL;
145 pxTopOfStack--;
146 *pxTopOfStack = ( StackType_t ) NULL;
147 pxTopOfStack--;
148 *pxTopOfStack = ( StackType_t ) NULL;
149 pxTopOfStack--;
150 *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;
151
152 if( ( ( uint32_t ) pxCode & portTHUMB_MODE_ADDRESS ) != 0x00UL )
153 {
154 /* The task will start in THUMB mode. */
155 *pxTopOfStack |= portTHUMB_MODE_BIT;
156 }
157
158 pxTopOfStack--;
159
160 /* Next the return address, which in this case is the start of the task. */
161 *pxTopOfStack = ( StackType_t ) pxCode;
162 pxTopOfStack--;
163
164 /* Next all the registers other than the stack pointer. */
165 *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* R14 */
166 pxTopOfStack--;
167 *pxTopOfStack = ( StackType_t ) 0x12121212; /* R12 */
168 pxTopOfStack--;
169 *pxTopOfStack = ( StackType_t ) 0x11111111; /* R11 */
170 pxTopOfStack--;
171 *pxTopOfStack = ( StackType_t ) 0x10101010; /* R10 */
172 pxTopOfStack--;
173 *pxTopOfStack = ( StackType_t ) 0x09090909; /* R9 */
174 pxTopOfStack--;
175 *pxTopOfStack = ( StackType_t ) 0x08080808; /* R8 */
176 pxTopOfStack--;
177 *pxTopOfStack = ( StackType_t ) 0x07070707; /* R7 */
178 pxTopOfStack--;
179 *pxTopOfStack = ( StackType_t ) 0x06060606; /* R6 */
180 pxTopOfStack--;
181 *pxTopOfStack = ( StackType_t ) 0x05050505; /* R5 */
182 pxTopOfStack--;
183 *pxTopOfStack = ( StackType_t ) 0x04040404; /* R4 */
184 pxTopOfStack--;
185 *pxTopOfStack = ( StackType_t ) 0x03030303; /* R3 */
186 pxTopOfStack--;
187 *pxTopOfStack = ( StackType_t ) 0x02020202; /* R2 */
188 pxTopOfStack--;
189 *pxTopOfStack = ( StackType_t ) 0x01010101; /* R1 */
190 pxTopOfStack--;
191 *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
192 pxTopOfStack--;
193
194 /* The task will start with a critical nesting count of 0 as interrupts are
195 * enabled. */
196 *pxTopOfStack = portNO_CRITICAL_NESTING;
197
198 #if ( configUSE_TASK_FPU_SUPPORT == 1 )
199 {
200 /* The task will start without a floating point context. A task that uses
201 * the floating point hardware must call vPortTaskUsesFPU() before executing
202 * any floating point instructions. */
203 pxTopOfStack--;
204 *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
205 }
206 #elif ( configUSE_TASK_FPU_SUPPORT == 2 )
207 {
208 /* The task will start with a floating point context. Leave enough
209 * space for the registers - and ensure they are initialised to 0. */
210 pxTopOfStack -= portFPU_REGISTER_WORDS;
211 memset( pxTopOfStack, 0x00, portFPU_REGISTER_WORDS * sizeof( StackType_t ) );
212
213 /* Initialise the slot containing ulPortTaskHasFPUContext to true as
214 * the task starts with a floating point context. */
215 pxTopOfStack--;
216 *pxTopOfStack = pdTRUE;
217 ulPortTaskHasFPUContext = pdTRUE;
218 }
219 #else
220 {
221 #error "Invalid configUSE_TASK_FPU_SUPPORT value - configUSE_TASK_FPU_SUPPORT must be set to 1, 2, or left undefined."
222 }
223 #endif /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */
224
225 return pxTopOfStack;
226 }
227 /*-----------------------------------------------------------*/
228
prvTaskExitError(void)229 static void prvTaskExitError( void )
230 {
231 /* A function that implements a task must not exit or attempt to return to
232 * its caller as there is nothing to return to. If a task wants to exit it
233 * should instead call vTaskDelete( NULL ).
234 *
235 * Artificially force an assert() to be triggered if configASSERT() is
236 * defined, then stop here so application writers can catch the error. */
237 configASSERT( ulPortInterruptNesting == ~0UL );
238 portDISABLE_INTERRUPTS();
239
240 for( ; ; )
241 {
242 }
243 }
244 /*-----------------------------------------------------------*/
245
xPortStartScheduler(void)246 BaseType_t xPortStartScheduler( void )
247 {
248 uint32_t ulAPSR;
249
250 /* Only continue if the CPU is not in User mode. The CPU must be in a
251 * Privileged mode for the scheduler to start. */
252 ulAPSR = ulReadAPSR();
253
254 ulAPSR &= portAPSR_MODE_BITS_MASK;
255 configASSERT( ulAPSR != portAPSR_USER_MODE );
256
257 if( ulAPSR != portAPSR_USER_MODE )
258 {
259 /* Start the timer that generates the tick ISR. */
260 portDISABLE_INTERRUPTS();
261 configSETUP_TICK_INTERRUPT();
262
263 /* Start the first task executing. */
264 vPortRestoreTaskContext();
265 }
266
267 /* Will only get here if vTaskStartScheduler() was called with the CPU in
268 * a non-privileged mode or the binary point register was not set to its lowest
269 * possible value. prvTaskExitError() is referenced to prevent a compiler
270 * warning about it being defined but not referenced in the case that the user
271 * defines their own exit address. */
272 ( void ) prvTaskExitError;
273 return 0;
274 }
275 /*-----------------------------------------------------------*/
276
vPortEndScheduler(void)277 void vPortEndScheduler( void )
278 {
279 /* Not implemented in ports where there is nothing to return to.
280 * Artificially force an assert. */
281 configASSERT( ulCriticalNesting == 1000UL );
282 }
283 /*-----------------------------------------------------------*/
284
vPortEnterCritical(void)285 void vPortEnterCritical( void )
286 {
287 portDISABLE_INTERRUPTS();
288
289 /* Now that interrupts are disabled, ulCriticalNesting can be accessed
290 * directly. Increment ulCriticalNesting to keep a count of how many times
291 * portENTER_CRITICAL() has been called. */
292 ulCriticalNesting++;
293
294 /* This is not the interrupt safe version of the enter critical function so
295 * assert() if it is being called from an interrupt context. Only API
296 * functions that end in "FromISR" can be used in an interrupt. Only assert if
297 * the critical nesting count is 1 to protect against recursive calls if the
298 * assert function also uses a critical section. */
299 if( ulCriticalNesting == 1 )
300 {
301 configASSERT( ulPortInterruptNesting == 0 );
302 }
303 }
304 /*-----------------------------------------------------------*/
305
vPortExitCritical(void)306 void vPortExitCritical( void )
307 {
308 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
309 {
310 /* Decrement the nesting count as the critical section is being
311 * exited. */
312 ulCriticalNesting--;
313
314 /* If the nesting level has reached zero then all interrupt
315 * priorities must be re-enabled. */
316 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
317 {
318 /* Critical nesting has reached zero so all interrupt priorities
319 * should be unmasked. */
320 portENABLE_INTERRUPTS();
321 }
322 }
323 }
324 /*-----------------------------------------------------------*/
325
FreeRTOS_Tick_Handler(void)326 void FreeRTOS_Tick_Handler( void )
327 {
328 uint32_t ulInterruptStatus;
329
330 ulInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
331
332 /* Increment the RTOS tick. */
333 if( xTaskIncrementTick() != pdFALSE )
334 {
335 ulPortYieldRequired = pdTRUE;
336 }
337
338 portCLEAR_INTERRUPT_MASK_FROM_ISR( ulInterruptStatus );
339
340 configCLEAR_TICK_INTERRUPT();
341 }
342 /*-----------------------------------------------------------*/
343
344 #if ( configUSE_TASK_FPU_SUPPORT != 2 )
345
vPortTaskUsesFPU(void)346 void vPortTaskUsesFPU( void )
347 {
348 /* A task is registering the fact that it needs an FPU context. Set the
349 * FPU flag (which is saved as part of the task context). */
350 ulPortTaskHasFPUContext = pdTRUE;
351
352 /* Initialise the floating point status register. */
353 vPortInitialiseFPSCR();
354 }
355
356 #endif /* configUSE_TASK_FPU_SUPPORT */
357 /*-----------------------------------------------------------*/
358