1 /*
2 * FreeRTOS Kernel V11.1.0
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 /* IAR includes. */
30 #include <intrinsics.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. A default that uses the PIT is provided in the official demo application.
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. A default that uses the PIT is provided in the official demo application.
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, interrupts 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 /*-----------------------------------------------------------*/
75
76 /*
77 * Starts the first task executing. This function is necessarily written in
78 * assembly code so is implemented in portASM.s.
79 */
80 extern void vPortRestoreTaskContext( void );
81
82 /*
83 * Used to catch tasks that attempt to return from their implementing function.
84 */
85 static void prvTaskExitError( void );
86
87 /*-----------------------------------------------------------*/
88
89 /* A variable is used to keep track of the critical section nesting. This
90 * variable has to be stored as part of the task context and must be initialised to
91 * a non zero value to ensure interrupts don't inadvertently become unmasked before
92 * the scheduler starts. As it is stored as part of the task context it will
93 * automatically be set to 0 when the first task is started. */
94 volatile uint32_t ulCriticalNesting = 9999UL;
95
96 /* Saved as part of the task context. If ulPortTaskHasFPUContext is non-zero
97 * then a floating point context must be saved and restored for the task. */
98 uint32_t ulPortTaskHasFPUContext = pdFALSE;
99
100 /* Set to 1 to pend a context switch from an ISR. */
101 uint32_t ulPortYieldRequired = pdFALSE;
102
103 /* Counts the interrupt nesting depth. A context switch is only performed if
104 * if the nesting depth is 0. */
105 uint32_t ulPortInterruptNesting = 0UL;
106
107
108 /*-----------------------------------------------------------*/
109
110 /*
111 * See header file for description.
112 */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)113 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
114 TaskFunction_t pxCode,
115 void * pvParameters )
116 {
117 /* Setup the initial stack of the task. The stack is set exactly as
118 * expected by the portRESTORE_CONTEXT() macro.
119 *
120 * The fist real value on the stack is the status register, which is set for
121 * system mode, with interrupts enabled. A few NULLs are added first to ensure
122 * GDB does not try decoding a non-existent return address. */
123 *pxTopOfStack = NULL;
124 pxTopOfStack--;
125 *pxTopOfStack = NULL;
126 pxTopOfStack--;
127 *pxTopOfStack = NULL;
128 pxTopOfStack--;
129 *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;
130
131 if( ( ( uint32_t ) pxCode & portTHUMB_MODE_ADDRESS ) != 0x00UL )
132 {
133 /* The task will start in THUMB mode. */
134 *pxTopOfStack |= portTHUMB_MODE_BIT;
135 }
136
137 pxTopOfStack--;
138
139 /* Next the return address, which in this case is the start of the task. */
140 *pxTopOfStack = ( StackType_t ) pxCode;
141 pxTopOfStack--;
142
143 /* Next all the registers other than the stack pointer. */
144 *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* R14 */
145 pxTopOfStack--;
146 *pxTopOfStack = ( StackType_t ) 0x12121212; /* R12 */
147 pxTopOfStack--;
148 *pxTopOfStack = ( StackType_t ) 0x11111111; /* R11 */
149 pxTopOfStack--;
150 *pxTopOfStack = ( StackType_t ) 0x10101010; /* R10 */
151 pxTopOfStack--;
152 *pxTopOfStack = ( StackType_t ) 0x09090909; /* R9 */
153 pxTopOfStack--;
154 *pxTopOfStack = ( StackType_t ) 0x08080808; /* R8 */
155 pxTopOfStack--;
156 *pxTopOfStack = ( StackType_t ) 0x07070707; /* R7 */
157 pxTopOfStack--;
158 *pxTopOfStack = ( StackType_t ) 0x06060606; /* R6 */
159 pxTopOfStack--;
160 *pxTopOfStack = ( StackType_t ) 0x05050505; /* R5 */
161 pxTopOfStack--;
162 *pxTopOfStack = ( StackType_t ) 0x04040404; /* R4 */
163 pxTopOfStack--;
164 *pxTopOfStack = ( StackType_t ) 0x03030303; /* R3 */
165 pxTopOfStack--;
166 *pxTopOfStack = ( StackType_t ) 0x02020202; /* R2 */
167 pxTopOfStack--;
168 *pxTopOfStack = ( StackType_t ) 0x01010101; /* R1 */
169 pxTopOfStack--;
170 *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
171 pxTopOfStack--;
172
173 /* The task will start with a critical nesting count of 0 as interrupts are
174 * enabled. */
175 *pxTopOfStack = portNO_CRITICAL_NESTING;
176 pxTopOfStack--;
177
178 /* The task will start without a floating point context. A task that uses
179 * the floating point hardware must call vPortTaskUsesFPU() before executing
180 * any floating point instructions. */
181 *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
182
183 return pxTopOfStack;
184 }
185 /*-----------------------------------------------------------*/
186
prvTaskExitError(void)187 static void prvTaskExitError( void )
188 {
189 /* A function that implements a task must not exit or attempt to return to
190 * its caller as there is nothing to return to. If a task wants to exit it
191 * should instead call vTaskDelete( NULL ).
192 *
193 * Artificially force an assert() to be triggered if configASSERT() is
194 * defined, then stop here so application writers can catch the error. */
195 configASSERT( ulPortInterruptNesting == ~0UL );
196 portDISABLE_INTERRUPTS();
197
198 for( ; ; )
199 {
200 }
201 }
202 /*-----------------------------------------------------------*/
203
xPortStartScheduler(void)204 BaseType_t xPortStartScheduler( void )
205 {
206 uint32_t ulAPSR;
207
208 /* Only continue if the CPU is not in User mode. The CPU must be in a
209 * Privileged mode for the scheduler to start. */
210 __asm volatile ( "MRS %0, APSR" : "=r" ( ulAPSR ) );
211
212 ulAPSR &= portAPSR_MODE_BITS_MASK;
213 configASSERT( ulAPSR != portAPSR_USER_MODE );
214
215 if( ulAPSR != portAPSR_USER_MODE )
216 {
217 /* Start the timer that generates the tick ISR. */
218 configSETUP_TICK_INTERRUPT();
219 vPortRestoreTaskContext();
220 }
221
222 /* Will only get here if vTaskStartScheduler() was called with the CPU in
223 * a non-privileged mode or the binary point register was not set to its lowest
224 * possible value. */
225 return 0;
226 }
227 /*-----------------------------------------------------------*/
228
vPortEndScheduler(void)229 void vPortEndScheduler( void )
230 {
231 /* Not implemented in ports where there is nothing to return to.
232 * Artificially force an assert. */
233 configASSERT( ulCriticalNesting == 1000UL );
234 }
235 /*-----------------------------------------------------------*/
236
vPortEnterCritical(void)237 void vPortEnterCritical( void )
238 {
239 portDISABLE_INTERRUPTS();
240
241 /* Now that interrupts are disabled, ulCriticalNesting can be accessed
242 * directly. Increment ulCriticalNesting to keep a count of how many times
243 * portENTER_CRITICAL() has been called. */
244 ulCriticalNesting++;
245
246 /* This is not the interrupt safe version of the enter critical function so
247 * assert() if it is being called from an interrupt context. Only API
248 * functions that end in "FromISR" can be used in an interrupt. Only assert if
249 * the critical nesting count is 1 to protect against recursive calls if the
250 * assert function also uses a critical section. */
251 if( ulCriticalNesting == 1 )
252 {
253 configASSERT( ulPortInterruptNesting == 0 );
254 }
255 }
256 /*-----------------------------------------------------------*/
257
vPortExitCritical(void)258 void vPortExitCritical( void )
259 {
260 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
261 {
262 /* Decrement the nesting count as the critical section is being
263 * exited. */
264 ulCriticalNesting--;
265
266 /* If the nesting level has reached zero then all interrupt
267 * priorities must be re-enabled. */
268 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
269 {
270 /* Critical nesting has reached zero so all interrupt priorities
271 * should be unmasked. */
272 portENABLE_INTERRUPTS();
273 }
274 }
275 }
276 /*-----------------------------------------------------------*/
277
FreeRTOS_Tick_Handler(void)278 void FreeRTOS_Tick_Handler( void )
279 {
280 portDISABLE_INTERRUPTS();
281
282 /* Increment the RTOS tick. */
283 if( xTaskIncrementTick() != pdFALSE )
284 {
285 ulPortYieldRequired = pdTRUE;
286 }
287
288 portENABLE_INTERRUPTS();
289 configCLEAR_TICK_INTERRUPT();
290 }
291 /*-----------------------------------------------------------*/
292
vPortTaskUsesFPU(void)293 void vPortTaskUsesFPU( void )
294 {
295 uint32_t ulInitialFPSCR = 0;
296
297 /* A task is registering the fact that it needs an FPU context. Set the
298 * FPU flag (which is saved as part of the task context). */
299 ulPortTaskHasFPUContext = pdTRUE;
300
301 /* Initialise the floating point status register. */
302 __asm( "FMXR FPSCR, %0" ::"r" ( ulInitialFPSCR ) );
303 }
304 /*-----------------------------------------------------------*/
305