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