]> begriffs open source - freertos/blob - portable/IAR/ARM_CM0/port.c
[AUTO][RELEASE]: Bump file header version to "10.4.3 LTS Patch 3"
[freertos] / portable / IAR / ARM_CM0 / port.c
1 /*\r
2  * FreeRTOS Kernel V10.4.3 LTS Patch 3\r
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * https://www.FreeRTOS.org\r
23  * https://github.com/FreeRTOS\r
24  *\r
25  */\r
26 \r
27 /*-----------------------------------------------------------\r
28 * Implementation of functions defined in portable.h for the ARM CM0 port.\r
29 *----------------------------------------------------------*/\r
30 \r
31 /* IAR includes. */\r
32 #include "intrinsics.h"\r
33 \r
34 /* Scheduler includes. */\r
35 #include "FreeRTOS.h"\r
36 #include "task.h"\r
37 \r
38 /* Constants required to manipulate the NVIC. */\r
39 #define portNVIC_SYSTICK_CTRL_REG             ( *( ( volatile uint32_t * ) 0xe000e010 ) )\r
40 #define portNVIC_SYSTICK_LOAD_REG             ( *( ( volatile uint32_t * ) 0xe000e014 ) )\r
41 #define portNVIC_SYSTICK_CURRENT_VALUE_REG    ( *( ( volatile uint32_t * ) 0xe000e018 ) )\r
42 #define portNVIC_INT_CTRL_REG                 ( *( ( volatile uint32_t * ) 0xe000ed04 ) )\r
43 #define portNVIC_SHPR3_REG                    ( *( ( volatile uint32_t * ) 0xe000ed20 ) )\r
44 #define portNVIC_SYSTICK_CLK_BIT              ( 1UL << 2UL )\r
45 #define portNVIC_SYSTICK_INT_BIT              ( 1UL << 1UL )\r
46 #define portNVIC_SYSTICK_ENABLE_BIT           ( 1UL << 0UL )\r
47 #define portNVIC_SYSTICK_COUNT_FLAG_BIT       ( 1UL << 16UL )\r
48 #define portMIN_INTERRUPT_PRIORITY            ( 255UL )\r
49 #define portNVIC_PENDSV_PRI                   ( portMIN_INTERRUPT_PRIORITY << 16UL )\r
50 #define portNVIC_SYSTICK_PRI                  ( portMIN_INTERRUPT_PRIORITY << 24UL )\r
51 \r
52 /* Constants required to set up the initial stack. */\r
53 #define portINITIAL_XPSR                      ( 0x01000000 )\r
54 \r
55 /* For backward compatibility, ensure configKERNEL_INTERRUPT_PRIORITY is\r
56  * defined.  The value 255 should also ensure backward compatibility.\r
57  * FreeRTOS.org versions prior to V4.3.0 did not include this definition. */\r
58 #ifndef configKERNEL_INTERRUPT_PRIORITY\r
59     #define configKERNEL_INTERRUPT_PRIORITY    0\r
60 #endif\r
61 \r
62 /* Each task maintains its own interrupt status in the critical nesting\r
63  * variable. */\r
64 static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;\r
65 \r
66 /* The systick is a 24-bit counter. */\r
67 #define portMAX_24_BIT_NUMBER    ( 0xffffffUL )\r
68 \r
69 /* A fiddle factor to estimate the number of SysTick counts that would have\r
70  * occurred while the SysTick counter is stopped during tickless idle\r
71  * calculations. */\r
72 #ifndef portMISSED_COUNTS_FACTOR\r
73     #define portMISSED_COUNTS_FACTOR    ( 45UL )\r
74 #endif\r
75 \r
76 /* The number of SysTick increments that make up one tick period. */\r
77 #if ( configUSE_TICKLESS_IDLE == 1 )\r
78     static uint32_t ulTimerCountsForOneTick = 0;\r
79 #endif /* configUSE_TICKLESS_IDLE */\r
80 \r
81 /* The maximum number of tick periods that can be suppressed is limited by the\r
82  * 24 bit resolution of the SysTick timer. */\r
83 #if ( configUSE_TICKLESS_IDLE == 1 )\r
84     static uint32_t xMaximumPossibleSuppressedTicks = 0;\r
85 #endif /* configUSE_TICKLESS_IDLE */\r
86 \r
87 /* Compensate for the CPU cycles that pass while the SysTick is stopped (low\r
88  * power functionality only. */\r
89 #if ( configUSE_TICKLESS_IDLE == 1 )\r
90     static uint32_t ulStoppedTimerCompensation = 0;\r
91 #endif /* configUSE_TICKLESS_IDLE */\r
92 \r
93 /*\r
94  * Setup the timer to generate the tick interrupts.  The implementation in this\r
95  * file is weak to allow application writers to change the timer used to\r
96  * generate the tick interrupt.\r
97  */\r
98 void vPortSetupTimerInterrupt( void );\r
99 \r
100 /*\r
101  * Exception handlers.\r
102  */\r
103 void xPortSysTickHandler( void );\r
104 \r
105 /*\r
106  * Start first task is a separate function so it can be tested in isolation.\r
107  */\r
108 extern void vPortStartFirstTask( void );\r
109 \r
110 /*\r
111  * Used to catch tasks that attempt to return from their implementing function.\r
112  */\r
113 static void prvTaskExitError( void );\r
114 \r
115 /*-----------------------------------------------------------*/\r
116 \r
117 /*\r
118  * See header file for description.\r
119  */\r
120 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,\r
121                                      TaskFunction_t pxCode,\r
122                                      void * pvParameters )\r
123 {\r
124     /* Simulate the stack frame as it would be created by a context switch\r
125      * interrupt. */\r
126     pxTopOfStack--;                                   /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */\r
127     *pxTopOfStack = portINITIAL_XPSR;                 /* xPSR */\r
128     pxTopOfStack--;\r
129     *pxTopOfStack = ( StackType_t ) pxCode;           /* PC */\r
130     pxTopOfStack--;\r
131     *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */\r
132     pxTopOfStack -= 5;                                /* R12, R3, R2 and R1. */\r
133     *pxTopOfStack = ( StackType_t ) pvParameters;     /* R0 */\r
134     pxTopOfStack -= 8;                                /* R11..R4. */\r
135 \r
136     return pxTopOfStack;\r
137 }\r
138 /*-----------------------------------------------------------*/\r
139 \r
140 static void prvTaskExitError( void )\r
141 {\r
142     /* A function that implements a task must not exit or attempt to return to\r
143      * its caller as there is nothing to return to.  If a task wants to exit it\r
144      * should instead call vTaskDelete( NULL ).\r
145      *\r
146      * Artificially force an assert() to be triggered if configASSERT() is\r
147      * defined, then stop here so application writers can catch the error. */\r
148     configASSERT( uxCriticalNesting == ~0UL );\r
149     portDISABLE_INTERRUPTS();\r
150 \r
151     for( ; ; )\r
152     {\r
153     }\r
154 }\r
155 /*-----------------------------------------------------------*/\r
156 \r
157 /*\r
158  * See header file for description.\r
159  */\r
160 BaseType_t xPortStartScheduler( void )\r
161 {\r
162     /* Make PendSV and SysTick the lowest priority interrupts. */\r
163     portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;\r
164     portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;\r
165 \r
166     /* Start the timer that generates the tick ISR.  Interrupts are disabled\r
167      * here already. */\r
168     vPortSetupTimerInterrupt();\r
169 \r
170     /* Initialise the critical nesting count ready for the first task. */\r
171     uxCriticalNesting = 0;\r
172 \r
173     /* Start the first task. */\r
174     vPortStartFirstTask();\r
175 \r
176     /* Should not get here! */\r
177     return 0;\r
178 }\r
179 /*-----------------------------------------------------------*/\r
180 \r
181 void vPortEndScheduler( void )\r
182 {\r
183     /* Not implemented in ports where there is nothing to return to.\r
184      * Artificially force an assert. */\r
185     configASSERT( uxCriticalNesting == 1000UL );\r
186 }\r
187 /*-----------------------------------------------------------*/\r
188 \r
189 void vPortYield( void )\r
190 {\r
191     /* Set a PendSV to request a context switch. */\r
192     portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET;\r
193 \r
194     /* Barriers are normally not required but do ensure the code is completely\r
195      * within the specified behaviour for the architecture. */\r
196     __DSB();\r
197     __ISB();\r
198 }\r
199 /*-----------------------------------------------------------*/\r
200 \r
201 void vPortEnterCritical( void )\r
202 {\r
203     portDISABLE_INTERRUPTS();\r
204     uxCriticalNesting++;\r
205     __DSB();\r
206     __ISB();\r
207 }\r
208 /*-----------------------------------------------------------*/\r
209 \r
210 void vPortExitCritical( void )\r
211 {\r
212     configASSERT( uxCriticalNesting );\r
213     uxCriticalNesting--;\r
214 \r
215     if( uxCriticalNesting == 0 )\r
216     {\r
217         portENABLE_INTERRUPTS();\r
218     }\r
219 }\r
220 /*-----------------------------------------------------------*/\r
221 \r
222 void xPortSysTickHandler( void )\r
223 {\r
224     uint32_t ulPreviousMask;\r
225 \r
226     ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();\r
227     {\r
228         /* Increment the RTOS tick. */\r
229         if( xTaskIncrementTick() != pdFALSE )\r
230         {\r
231             /* Pend a context switch. */\r
232             portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET;\r
233         }\r
234     }\r
235     portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );\r
236 }\r
237 /*-----------------------------------------------------------*/\r
238 \r
239 /*\r
240  * Setup the systick timer to generate the tick interrupts at the required\r
241  * frequency.\r
242  */\r
243 __weak void vPortSetupTimerInterrupt( void )\r
244 {\r
245     /* Calculate the constants required to configure the tick interrupt. */\r
246     #if ( configUSE_TICKLESS_IDLE == 1 )\r
247         {\r
248             ulTimerCountsForOneTick = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ );\r
249             xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;\r
250             ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR;\r
251         }\r
252     #endif /* configUSE_TICKLESS_IDLE */\r
253 \r
254     /* Stop and reset the SysTick. */\r
255     portNVIC_SYSTICK_CTRL_REG = 0UL;\r
256     portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;\r
257 \r
258     /* Configure SysTick to interrupt at the requested rate. */\r
259     portNVIC_SYSTICK_LOAD_REG = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;\r
260     portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;\r
261 }\r
262 /*-----------------------------------------------------------*/\r
263 \r
264 #if ( configUSE_TICKLESS_IDLE == 1 )\r
265 \r
266     __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )\r
267     {\r
268         uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements;\r
269         TickType_t xModifiableIdleTime;\r
270 \r
271         /* Make sure the SysTick reload value does not overflow the counter. */\r
272         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )\r
273         {\r
274             xExpectedIdleTime = xMaximumPossibleSuppressedTicks;\r
275         }\r
276 \r
277         /* Stop the SysTick momentarily.  The time the SysTick is stopped for\r
278          * is accounted for as best it can be, but using the tickless mode will\r
279          * inevitably result in some tiny drift of the time maintained by the\r
280          * kernel with respect to calendar time. */\r
281         portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;\r
282 \r
283         /* Calculate the reload value required to wait xExpectedIdleTime\r
284          * tick periods.  -1 is used because this code will execute part way\r
285          * through one of the tick periods. */\r
286         ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );\r
287 \r
288         if( ulReloadValue > ulStoppedTimerCompensation )\r
289         {\r
290             ulReloadValue -= ulStoppedTimerCompensation;\r
291         }\r
292 \r
293         /* Enter a critical section but don't use the taskENTER_CRITICAL()\r
294          * method as that will mask interrupts that should exit sleep mode. */\r
295         __disable_interrupt();\r
296         __DSB();\r
297         __ISB();\r
298 \r
299         /* If a context switch is pending or a task is waiting for the scheduler\r
300          * to be unsuspended then abandon the low power entry. */\r
301         if( eTaskConfirmSleepModeStatus() == eAbortSleep )\r
302         {\r
303             /* Restart from whatever is left in the count register to complete\r
304              * this tick period. */\r
305             portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;\r
306 \r
307             /* Restart SysTick. */\r
308             portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;\r
309 \r
310             /* Reset the reload register to the value required for normal tick\r
311              * periods. */\r
312             portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;\r
313 \r
314             /* Re-enable interrupts - see comments above __disable_interrupt()\r
315              * call above. */\r
316             __enable_interrupt();\r
317         }\r
318         else\r
319         {\r
320             /* Set the new reload value. */\r
321             portNVIC_SYSTICK_LOAD_REG = ulReloadValue;\r
322 \r
323             /* Clear the SysTick count flag and set the count value back to\r
324              * zero. */\r
325             portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;\r
326 \r
327             /* Restart SysTick. */\r
328             portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;\r
329 \r
330             /* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can\r
331              * set its parameter to 0 to indicate that its implementation contains\r
332              * its own wait for interrupt or wait for event instruction, and so wfi\r
333              * should not be executed again.  However, the original expected idle\r
334              * time variable must remain unmodified, so a copy is taken. */\r
335             xModifiableIdleTime = xExpectedIdleTime;\r
336             configPRE_SLEEP_PROCESSING( xModifiableIdleTime );\r
337 \r
338             if( xModifiableIdleTime > 0 )\r
339             {\r
340                 __DSB();\r
341                 __WFI();\r
342                 __ISB();\r
343             }\r
344 \r
345             configPOST_SLEEP_PROCESSING( xExpectedIdleTime );\r
346 \r
347             /* Re-enable interrupts to allow the interrupt that brought the MCU\r
348              * out of sleep mode to execute immediately.  see comments above\r
349              * __disable_interrupt() call above. */\r
350             __enable_interrupt();\r
351             __DSB();\r
352             __ISB();\r
353 \r
354             /* Disable interrupts again because the clock is about to be stopped\r
355              * and interrupts that execute while the clock is stopped will increase\r
356              * any slippage between the time maintained by the RTOS and calendar\r
357              * time. */\r
358             __disable_interrupt();\r
359             __DSB();\r
360             __ISB();\r
361 \r
362             /* Disable the SysTick clock without reading the\r
363              * portNVIC_SYSTICK_CTRL_REG register to ensure the\r
364              * portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set.  Again,\r
365              * the time the SysTick is stopped for is accounted for as best it can\r
366              * be, but using the tickless mode will inevitably result in some tiny\r
367              * drift of the time maintained by the kernel with respect to calendar\r
368              * time*/\r
369             portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT );\r
370 \r
371             /* Determine if the SysTick clock has already counted to zero and\r
372              * been set back to the current reload value (the reload back being\r
373              * correct for the entire expected idle time) or if the SysTick is yet\r
374              * to count to zero (in which case an interrupt other than the SysTick\r
375              * must have brought the system out of sleep mode). */\r
376             if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )\r
377             {\r
378                 uint32_t ulCalculatedLoadValue;\r
379 \r
380                 /* The tick interrupt is already pending, and the SysTick count\r
381                  * reloaded with ulReloadValue.  Reset the\r
382                  * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick\r
383                  * period. */\r
384                 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );\r
385 \r
386                 /* Don't allow a tiny value, or values that have somehow\r
387                  * underflowed because the post sleep hook did something\r
388                  * that took too long. */\r
389                 if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )\r
390                 {\r
391                     ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );\r
392                 }\r
393 \r
394                 portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;\r
395 \r
396                 /* As the pending tick will be processed as soon as this\r
397                  * function exits, the tick value maintained by the tick is stepped\r
398                  * forward by one less than the time spent waiting. */\r
399                 ulCompleteTickPeriods = xExpectedIdleTime - 1UL;\r
400             }\r
401             else\r
402             {\r
403                 /* Something other than the tick interrupt ended the sleep.\r
404                  * Work out how long the sleep lasted rounded to complete tick\r
405                  * periods (not the ulReload value which accounted for part\r
406                  * ticks). */\r
407                 ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;\r
408 \r
409                 /* How many complete tick periods passed while the processor\r
410                  * was waiting? */\r
411                 ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;\r
412 \r
413                 /* The reload value is set to whatever fraction of a single tick\r
414                  * period remains. */\r
415                 portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;\r
416             }\r
417 \r
418             /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG\r
419              * again, then set portNVIC_SYSTICK_LOAD_REG back to its standard\r
420              * value. */\r
421             portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;\r
422             portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;\r
423             vTaskStepTick( ulCompleteTickPeriods );\r
424             portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;\r
425 \r
426             /* Exit with interrpts enabled. */\r
427             __enable_interrupt();\r
428         }\r
429     }\r
430 \r
431 #endif /* configUSE_TICKLESS_IDLE */\r