xref: /Kernel-v10.6.2/portable/GCC/ARM_CRx_No_GIC/port.c (revision ef7b253b56c9788077f5ecd6c9deb4021923d646)
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