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 /* IAR includes. */
33 #include <intrinsics.h>
34
35 /* Scheduler includes. */
36 #include "FreeRTOS.h"
37 #include "task.h"
38
39 #ifndef configINTERRUPT_CONTROLLER_BASE_ADDRESS
40 #error configINTERRUPT_CONTROLLER_BASE_ADDRESS must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
41 #endif
42
43 #ifndef configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET
44 #error configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
45 #endif
46
47 #ifndef configUNIQUE_INTERRUPT_PRIORITIES
48 #error configUNIQUE_INTERRUPT_PRIORITIES must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
49 #endif
50
51 #ifndef configSETUP_TICK_INTERRUPT
52 #error configSETUP_TICK_INTERRUPT() must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
53 #endif /* configSETUP_TICK_INTERRUPT */
54
55 #ifndef configMAX_API_CALL_INTERRUPT_PRIORITY
56 #error configMAX_API_CALL_INTERRUPT_PRIORITY must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
57 #endif
58
59 #if configMAX_API_CALL_INTERRUPT_PRIORITY == 0
60 #error configMAX_API_CALL_INTERRUPT_PRIORITY must not be set to 0
61 #endif
62
63 #if configMAX_API_CALL_INTERRUPT_PRIORITY > configUNIQUE_INTERRUPT_PRIORITIES
64 #error configMAX_API_CALL_INTERRUPT_PRIORITY must be less than or equal to configUNIQUE_INTERRUPT_PRIORITIES as the lower the numeric priority value the higher the logical interrupt priority
65 #endif
66
67 #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
68 /* Check the configuration. */
69 #if( configMAX_PRIORITIES > 32 )
70 #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.
71 #endif
72 #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
73
74 /* In case security extensions are implemented. */
75 #if configMAX_API_CALL_INTERRUPT_PRIORITY <= ( configUNIQUE_INTERRUPT_PRIORITIES / 2 )
76 #error configMAX_API_CALL_INTERRUPT_PRIORITY must be greater than ( configUNIQUE_INTERRUPT_PRIORITIES / 2 )
77 #endif
78
79 #ifndef configCLEAR_TICK_INTERRUPT
80 #define configCLEAR_TICK_INTERRUPT()
81 #endif
82
83 /* A critical section is exited when the critical section nesting count reaches
84 this value. */
85 #define portNO_CRITICAL_NESTING ( ( uint32_t ) 0 )
86
87 /* In all GICs 255 can be written to the priority mask register to unmask all
88 (but the lowest) interrupt priority. */
89 #define portUNMASK_VALUE ( 0xFFUL )
90
91 /* Tasks are not created with a floating point context, but can be given a
92 floating point context after they have been created. A variable is stored as
93 part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task
94 does not have an FPU context, or any other value if the task does have an FPU
95 context. */
96 #define portNO_FLOATING_POINT_CONTEXT ( ( StackType_t ) 0 )
97
98 /* Constants required to setup the initial task context. */
99 #define portINITIAL_SPSR ( ( StackType_t ) 0x1f ) /* System mode, ARM mode, interrupts enabled. */
100 #define portTHUMB_MODE_BIT ( ( StackType_t ) 0x20 )
101 #define portTHUMB_MODE_ADDRESS ( 0x01UL )
102
103 /* Used by portASSERT_IF_INTERRUPT_PRIORITY_INVALID() when ensuring the binary
104 point is zero. */
105 #define portBINARY_POINT_BITS ( ( uint8_t ) 0x03 )
106
107 /* Masks all bits in the APSR other than the mode bits. */
108 #define portAPSR_MODE_BITS_MASK ( 0x1F )
109
110 /* The value of the mode bits in the APSR when the CPU is executing in user
111 mode. */
112 #define portAPSR_USER_MODE ( 0x10 )
113
114 /* Macro to unmask all interrupt priorities. */
115 #define portCLEAR_INTERRUPT_MASK() \
116 { \
117 __disable_irq(); \
118 portICCPMR_PRIORITY_MASK_REGISTER = portUNMASK_VALUE; \
119 __asm( "DSB \n" \
120 "ISB \n" ); \
121 __enable_irq(); \
122 }
123
124 /*-----------------------------------------------------------*/
125
126 /*
127 * Starts the first task executing. This function is necessarily written in
128 * assembly code so is implemented in portASM.s.
129 */
130 extern void vPortRestoreTaskContext( void );
131
132 /*
133 * Used to catch tasks that attempt to return from their implementing function.
134 */
135 static void prvTaskExitError( void );
136
137 /*-----------------------------------------------------------*/
138
139 /* A variable is used to keep track of the critical section nesting. This
140 variable has to be stored as part of the task context and must be initialised to
141 a non zero value to ensure interrupts don't inadvertently become unmasked before
142 the scheduler starts. As it is stored as part of the task context it will
143 automatically be set to 0 when the first task is started. */
144 volatile uint32_t ulCriticalNesting = 9999UL;
145
146 /* Saved as part of the task context. If ulPortTaskHasFPUContext is non-zero
147 then a floating point context must be saved and restored for the task. */
148 uint32_t ulPortTaskHasFPUContext = pdFALSE;
149
150 /* Set to 1 to pend a context switch from an ISR. */
151 uint32_t ulPortYieldRequired = pdFALSE;
152
153 /* Counts the interrupt nesting depth. A context switch is only performed if
154 if the nesting depth is 0. */
155 uint32_t ulPortInterruptNesting = 0UL;
156
157
158 /*-----------------------------------------------------------*/
159
160 /*
161 * See header file for description.
162 */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)163 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
164 {
165 /* Setup the initial stack of the task. The stack is set exactly as
166 expected by the portRESTORE_CONTEXT() macro.
167
168 The fist real value on the stack is the status register, which is set for
169 system mode, with interrupts enabled. A few NULLs are added first to ensure
170 GDB does not try decoding a non-existent return address. */
171 *pxTopOfStack = NULL;
172 pxTopOfStack--;
173 *pxTopOfStack = NULL;
174 pxTopOfStack--;
175 *pxTopOfStack = NULL;
176 pxTopOfStack--;
177 *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;
178
179 if( ( ( uint32_t ) pxCode & portTHUMB_MODE_ADDRESS ) != 0x00UL )
180 {
181 /* The task will start in THUMB mode. */
182 *pxTopOfStack |= portTHUMB_MODE_BIT;
183 }
184
185 pxTopOfStack--;
186
187 /* Next the return address, which in this case is the start of the task. */
188 *pxTopOfStack = ( StackType_t ) pxCode;
189 pxTopOfStack--;
190
191 /* Next all the registers other than the stack pointer. */
192 *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* R14 */
193 pxTopOfStack--;
194 *pxTopOfStack = ( StackType_t ) 0x12121212; /* R12 */
195 pxTopOfStack--;
196 *pxTopOfStack = ( StackType_t ) 0x11111111; /* R11 */
197 pxTopOfStack--;
198 *pxTopOfStack = ( StackType_t ) 0x10101010; /* R10 */
199 pxTopOfStack--;
200 *pxTopOfStack = ( StackType_t ) 0x09090909; /* R9 */
201 pxTopOfStack--;
202 *pxTopOfStack = ( StackType_t ) 0x08080808; /* R8 */
203 pxTopOfStack--;
204 *pxTopOfStack = ( StackType_t ) 0x07070707; /* R7 */
205 pxTopOfStack--;
206 *pxTopOfStack = ( StackType_t ) 0x06060606; /* R6 */
207 pxTopOfStack--;
208 *pxTopOfStack = ( StackType_t ) 0x05050505; /* R5 */
209 pxTopOfStack--;
210 *pxTopOfStack = ( StackType_t ) 0x04040404; /* R4 */
211 pxTopOfStack--;
212 *pxTopOfStack = ( StackType_t ) 0x03030303; /* R3 */
213 pxTopOfStack--;
214 *pxTopOfStack = ( StackType_t ) 0x02020202; /* R2 */
215 pxTopOfStack--;
216 *pxTopOfStack = ( StackType_t ) 0x01010101; /* R1 */
217 pxTopOfStack--;
218 *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
219 pxTopOfStack--;
220
221 /* The task will start with a critical nesting count of 0 as interrupts are
222 enabled. */
223 *pxTopOfStack = portNO_CRITICAL_NESTING;
224 pxTopOfStack--;
225
226 /* The task will start without a floating point context. A task that uses
227 the floating point hardware must call vPortTaskUsesFPU() before executing
228 any floating point instructions. */
229 *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
230
231 return pxTopOfStack;
232 }
233 /*-----------------------------------------------------------*/
234
prvTaskExitError(void)235 static void prvTaskExitError( void )
236 {
237 /* A function that implements a task must not exit or attempt to return to
238 its caller as there is nothing to return to. If a task wants to exit it
239 should instead call vTaskDelete( NULL ).
240
241 Artificially force an assert() to be triggered if configASSERT() is
242 defined, then stop here so application writers can catch the error. */
243 configASSERT( ulPortInterruptNesting == ~0UL );
244 portDISABLE_INTERRUPTS();
245 for( ;; );
246 }
247 /*-----------------------------------------------------------*/
248
xPortStartScheduler(void)249 BaseType_t xPortStartScheduler( void )
250 {
251 uint32_t ulAPSR;
252
253 /* Only continue if the CPU is not in User mode. The CPU must be in a
254 Privileged mode for the scheduler to start. */
255 __asm volatile ( "MRS %0, APSR" : "=r" ( ulAPSR ) );
256 ulAPSR &= portAPSR_MODE_BITS_MASK;
257 configASSERT( ulAPSR != portAPSR_USER_MODE );
258
259 if( ulAPSR != portAPSR_USER_MODE )
260 {
261 /* Only continue if the binary point value is set to its lowest possible
262 setting. See the comments in vPortValidateInterruptPriority() below for
263 more information. */
264 configASSERT( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE );
265
266 if( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE )
267 {
268 /* Start the timer that generates the tick ISR. */
269 configSETUP_TICK_INTERRUPT();
270
271 __enable_irq();
272 vPortRestoreTaskContext();
273 }
274 }
275
276 /* Will only get here if vTaskStartScheduler() was called with the CPU in
277 a non-privileged mode or the binary point register was not set to its lowest
278 possible value. */
279 return 0;
280 }
281 /*-----------------------------------------------------------*/
282
vPortEndScheduler(void)283 void vPortEndScheduler( void )
284 {
285 /* Not implemented in ports where there is nothing to return to.
286 Artificially force an assert. */
287 configASSERT( ulCriticalNesting == 1000UL );
288 }
289 /*-----------------------------------------------------------*/
290
vPortEnterCritical(void)291 void vPortEnterCritical( void )
292 {
293 /* Disable interrupts as per portDISABLE_INTERRUPTS(); */
294 ulPortSetInterruptMask();
295
296 /* Now interrupts are disabled ulCriticalNesting can be accessed
297 directly. Increment ulCriticalNesting to keep a count of how many times
298 portENTER_CRITICAL() has been called. */
299 ulCriticalNesting++;
300
301 /* This is not the interrupt safe version of the enter critical function so
302 assert() if it is being called from an interrupt context. Only API
303 functions that end in "FromISR" can be used in an interrupt. Only assert if
304 the critical nesting count is 1 to protect against recursive calls if the
305 assert function also uses a critical section. */
306 if( ulCriticalNesting == 1 )
307 {
308 configASSERT( ulPortInterruptNesting == 0 );
309 }
310 }
311 /*-----------------------------------------------------------*/
312
vPortExitCritical(void)313 void vPortExitCritical( void )
314 {
315 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
316 {
317 /* Decrement the nesting count as the critical section is being
318 exited. */
319 ulCriticalNesting--;
320
321 /* If the nesting level has reached zero then all interrupt
322 priorities must be re-enabled. */
323 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
324 {
325 /* Critical nesting has reached zero so all interrupt priorities
326 should be unmasked. */
327 portCLEAR_INTERRUPT_MASK();
328 }
329 }
330 }
331 /*-----------------------------------------------------------*/
332
FreeRTOS_Tick_Handler(void)333 void FreeRTOS_Tick_Handler( void )
334 {
335 /* Set interrupt mask before altering scheduler structures. The tick
336 handler runs at the lowest priority, so interrupts cannot already be masked,
337 so there is no need to save and restore the current mask value. */
338 __disable_irq();
339 portICCPMR_PRIORITY_MASK_REGISTER = ( uint32_t ) ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT );
340 __asm( "DSB \n"
341 "ISB \n" );
342 __enable_irq();
343
344 /* Increment the RTOS tick. */
345 if( xTaskIncrementTick() != pdFALSE )
346 {
347 ulPortYieldRequired = pdTRUE;
348 }
349
350 /* Ensure all interrupt priorities are active again. */
351 portCLEAR_INTERRUPT_MASK();
352 configCLEAR_TICK_INTERRUPT();
353 }
354 /*-----------------------------------------------------------*/
355
vPortTaskUsesFPU(void)356 void vPortTaskUsesFPU( void )
357 {
358 uint32_t ulInitialFPSCR = 0;
359
360 /* A task is registering the fact that it needs an FPU context. Set the
361 FPU flag (which is saved as part of the task context). */
362 ulPortTaskHasFPUContext = pdTRUE;
363
364 /* Initialise the floating point status register. */
365 __asm( "FMXR FPSCR, %0" :: "r" (ulInitialFPSCR) );
366 }
367 /*-----------------------------------------------------------*/
368
vPortClearInterruptMask(uint32_t ulNewMaskValue)369 void vPortClearInterruptMask( uint32_t ulNewMaskValue )
370 {
371 if( ulNewMaskValue == pdFALSE )
372 {
373 portCLEAR_INTERRUPT_MASK();
374 }
375 }
376 /*-----------------------------------------------------------*/
377
ulPortSetInterruptMask(void)378 uint32_t ulPortSetInterruptMask( void )
379 {
380 uint32_t ulReturn;
381
382 __disable_irq();
383 if( portICCPMR_PRIORITY_MASK_REGISTER == ( uint32_t ) ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) )
384 {
385 /* Interrupts were already masked. */
386 ulReturn = pdTRUE;
387 }
388 else
389 {
390 ulReturn = pdFALSE;
391 portICCPMR_PRIORITY_MASK_REGISTER = ( uint32_t ) ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT );
392 __asm( "DSB \n"
393 "ISB \n" );
394 }
395 __enable_irq();
396
397 return ulReturn;
398 }
399 /*-----------------------------------------------------------*/
400
401 #if( configASSERT_DEFINED == 1 )
402
vPortValidateInterruptPriority(void)403 void vPortValidateInterruptPriority( void )
404 {
405 /* The following assertion will fail if a service routine (ISR) for
406 an interrupt that has been assigned a priority above
407 configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API
408 function. ISR safe FreeRTOS API functions must *only* be called
409 from interrupts that have been assigned a priority at or below
410 configMAX_SYSCALL_INTERRUPT_PRIORITY.
411
412 Numerically low interrupt priority numbers represent logically high
413 interrupt priorities, therefore the priority of the interrupt must
414 be set to a value equal to or numerically *higher* than
415 configMAX_SYSCALL_INTERRUPT_PRIORITY.
416
417 FreeRTOS maintains separate thread and ISR API functions to ensure
418 interrupt entry is as fast and simple as possible.
419
420 The following links provide detailed information:
421 https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html
422 https://www.FreeRTOS.org/FAQHelp.html */
423 configASSERT( portICCRPR_RUNNING_PRIORITY_REGISTER >= ( uint32_t ) ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) );
424
425 /* Priority grouping: The interrupt controller (GIC) allows the bits
426 that define each interrupt's priority to be split between bits that
427 define the interrupt's pre-emption priority bits and bits that define
428 the interrupt's sub-priority. For simplicity all bits must be defined
429 to be pre-emption priority bits. The following assertion will fail if
430 this is not the case (if some bits represent a sub-priority).
431
432 The priority grouping is configured by the GIC's binary point register
433 (ICCBPR). Writting 0 to ICCBPR will ensure it is set to its lowest
434 possible value (which may be above 0). */
435 configASSERT( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE );
436 }
437
438 #endif /* configASSERT_DEFINED */
439