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