xref: /Kernel-v10.6.2/portable/IAR/ARM_CM0/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 /*-----------------------------------------------------------
30 * Implementation of functions defined in portable.h for the ARM CM0 port.
31 *----------------------------------------------------------*/
32 
33 /* IAR includes. */
34 #include "intrinsics.h"
35 
36 /* Scheduler includes. */
37 #include "FreeRTOS.h"
38 #include "task.h"
39 
40 /* Constants required to manipulate the NVIC. */
41 #define portNVIC_SYSTICK_CTRL_REG             ( *( ( volatile uint32_t * ) 0xe000e010 ) )
42 #define portNVIC_SYSTICK_LOAD_REG             ( *( ( volatile uint32_t * ) 0xe000e014 ) )
43 #define portNVIC_SYSTICK_CURRENT_VALUE_REG    ( *( ( volatile uint32_t * ) 0xe000e018 ) )
44 #define portNVIC_INT_CTRL_REG                 ( *( ( volatile uint32_t * ) 0xe000ed04 ) )
45 #define portNVIC_SHPR3_REG                    ( *( ( volatile uint32_t * ) 0xe000ed20 ) )
46 #define portNVIC_SYSTICK_CLK_BIT              ( 1UL << 2UL )
47 #define portNVIC_SYSTICK_INT_BIT              ( 1UL << 1UL )
48 #define portNVIC_SYSTICK_ENABLE_BIT           ( 1UL << 0UL )
49 #define portNVIC_SYSTICK_COUNT_FLAG_BIT       ( 1UL << 16UL )
50 #define portNVIC_PEND_SYSTICK_SET_BIT         ( 1UL << 26UL )
51 #define portNVIC_PEND_SYSTICK_CLEAR_BIT       ( 1UL << 25UL )
52 #define portMIN_INTERRUPT_PRIORITY            ( 255UL )
53 #define portNVIC_PENDSV_PRI                   ( portMIN_INTERRUPT_PRIORITY << 16UL )
54 #define portNVIC_SYSTICK_PRI                  ( portMIN_INTERRUPT_PRIORITY << 24UL )
55 
56 /* Constants required to set up the initial stack. */
57 #define portINITIAL_XPSR                      ( 0x01000000 )
58 
59 /* Each task maintains its own interrupt status in the critical nesting
60  * variable. */
61 static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
62 
63 /* The systick is a 24-bit counter. */
64 #define portMAX_24_BIT_NUMBER    ( 0xffffffUL )
65 
66 /* A fiddle factor to estimate the number of SysTick counts that would have
67  * occurred while the SysTick counter is stopped during tickless idle
68  * calculations. */
69 #ifndef portMISSED_COUNTS_FACTOR
70     #define portMISSED_COUNTS_FACTOR    ( 94UL )
71 #endif
72 
73 /* The number of SysTick increments that make up one tick period. */
74 #if ( configUSE_TICKLESS_IDLE == 1 )
75     static uint32_t ulTimerCountsForOneTick = 0;
76 #endif /* configUSE_TICKLESS_IDLE */
77 
78 /* The maximum number of tick periods that can be suppressed is limited by the
79  * 24 bit resolution of the SysTick timer. */
80 #if ( configUSE_TICKLESS_IDLE == 1 )
81     static uint32_t xMaximumPossibleSuppressedTicks = 0;
82 #endif /* configUSE_TICKLESS_IDLE */
83 
84 /* Compensate for the CPU cycles that pass while the SysTick is stopped (low
85  * power functionality only. */
86 #if ( configUSE_TICKLESS_IDLE == 1 )
87     static uint32_t ulStoppedTimerCompensation = 0;
88 #endif /* configUSE_TICKLESS_IDLE */
89 
90 /* Let the user override the default SysTick clock rate.  If defined by the
91  * user, this symbol must equal the SysTick clock rate when the CLK bit is 0 in the
92  * configuration register. */
93 #ifndef configSYSTICK_CLOCK_HZ
94     #define configSYSTICK_CLOCK_HZ             ( configCPU_CLOCK_HZ )
95     /* Ensure the SysTick is clocked at the same frequency as the core. */
96     #define portNVIC_SYSTICK_CLK_BIT_CONFIG    ( portNVIC_SYSTICK_CLK_BIT )
97 #else
98     /* Select the option to clock SysTick not at the same frequency as the core. */
99     #define portNVIC_SYSTICK_CLK_BIT_CONFIG    ( 0 )
100 #endif
101 
102 /*
103  * Setup the timer to generate the tick interrupts.  The implementation in this
104  * file is weak to allow application writers to change the timer used to
105  * generate the tick interrupt.
106  */
107 void vPortSetupTimerInterrupt( void );
108 
109 /*
110  * Exception handlers.
111  */
112 void xPortSysTickHandler( void );
113 
114 /*
115  * Start first task is a separate function so it can be tested in isolation.
116  */
117 extern void vPortStartFirstTask( void );
118 
119 /*
120  * Used to catch tasks that attempt to return from their implementing function.
121  */
122 static void prvTaskExitError( void );
123 
124 /*-----------------------------------------------------------*/
125 
126 /*
127  * See header file for description.
128  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)129 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
130                                      TaskFunction_t pxCode,
131                                      void * pvParameters )
132 {
133     /* Simulate the stack frame as it would be created by a context switch
134      * interrupt. */
135     pxTopOfStack--;                                   /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
136     *pxTopOfStack = portINITIAL_XPSR;                 /* xPSR */
137     pxTopOfStack--;
138     *pxTopOfStack = ( StackType_t ) pxCode;           /* PC */
139     pxTopOfStack--;
140     *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */
141     pxTopOfStack -= 5;                                /* R12, R3, R2 and R1. */
142     *pxTopOfStack = ( StackType_t ) pvParameters;     /* R0 */
143     pxTopOfStack -= 8;                                /* R11..R4. */
144 
145     return pxTopOfStack;
146 }
147 /*-----------------------------------------------------------*/
148 
prvTaskExitError(void)149 static void prvTaskExitError( void )
150 {
151     /* A function that implements a task must not exit or attempt to return to
152      * its caller as there is nothing to return to.  If a task wants to exit it
153      * should instead call vTaskDelete( NULL ).
154      *
155      * Artificially force an assert() to be triggered if configASSERT() is
156      * defined, then stop here so application writers can catch the error. */
157     configASSERT( uxCriticalNesting == ~0UL );
158     portDISABLE_INTERRUPTS();
159 
160     for( ; ; )
161     {
162     }
163 }
164 /*-----------------------------------------------------------*/
165 
166 /*
167  * See header file for description.
168  */
xPortStartScheduler(void)169 BaseType_t xPortStartScheduler( void )
170 {
171     /* Make PendSV and SysTick the lowest priority interrupts. */
172     portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
173     portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
174 
175     /* Start the timer that generates the tick ISR.  Interrupts are disabled
176      * here already. */
177     vPortSetupTimerInterrupt();
178 
179     /* Initialise the critical nesting count ready for the first task. */
180     uxCriticalNesting = 0;
181 
182     /* Start the first task. */
183     vPortStartFirstTask();
184 
185     /* Should not get here! */
186     return 0;
187 }
188 /*-----------------------------------------------------------*/
189 
vPortEndScheduler(void)190 void vPortEndScheduler( void )
191 {
192     /* Not implemented in ports where there is nothing to return to.
193      * Artificially force an assert. */
194     configASSERT( uxCriticalNesting == 1000UL );
195 }
196 /*-----------------------------------------------------------*/
197 
vPortYield(void)198 void vPortYield( void )
199 {
200     /* Set a PendSV to request a context switch. */
201     portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET;
202 
203     /* Barriers are normally not required but do ensure the code is completely
204      * within the specified behaviour for the architecture. */
205     __DSB();
206     __ISB();
207 }
208 /*-----------------------------------------------------------*/
209 
vPortEnterCritical(void)210 void vPortEnterCritical( void )
211 {
212     portDISABLE_INTERRUPTS();
213     uxCriticalNesting++;
214     __DSB();
215     __ISB();
216 }
217 /*-----------------------------------------------------------*/
218 
vPortExitCritical(void)219 void vPortExitCritical( void )
220 {
221     configASSERT( uxCriticalNesting );
222     uxCriticalNesting--;
223 
224     if( uxCriticalNesting == 0 )
225     {
226         portENABLE_INTERRUPTS();
227     }
228 }
229 /*-----------------------------------------------------------*/
230 
xPortSysTickHandler(void)231 void xPortSysTickHandler( void )
232 {
233     uint32_t ulPreviousMask;
234 
235     ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();
236     {
237         /* Increment the RTOS tick. */
238         if( xTaskIncrementTick() != pdFALSE )
239         {
240             /* Pend a context switch. */
241             portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET;
242         }
243     }
244     portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );
245 }
246 /*-----------------------------------------------------------*/
247 
248 /*
249  * Setup the systick timer to generate the tick interrupts at the required
250  * frequency.
251  */
vPortSetupTimerInterrupt(void)252 __weak void vPortSetupTimerInterrupt( void )
253 {
254     /* Calculate the constants required to configure the tick interrupt. */
255     #if ( configUSE_TICKLESS_IDLE == 1 )
256     {
257         ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
258         xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
259         ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
260     }
261     #endif /* configUSE_TICKLESS_IDLE */
262 
263     /* Stop and reset the SysTick. */
264     portNVIC_SYSTICK_CTRL_REG = 0UL;
265     portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
266 
267     /* Configure SysTick to interrupt at the requested rate. */
268     portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
269     portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
270 }
271 /*-----------------------------------------------------------*/
272 
273 #if ( configUSE_TICKLESS_IDLE == 1 )
274 
vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)275     __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
276     {
277         uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft;
278         TickType_t xModifiableIdleTime;
279 
280         /* Make sure the SysTick reload value does not overflow the counter. */
281         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
282         {
283             xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
284         }
285 
286         /* Enter a critical section but don't use the taskENTER_CRITICAL()
287          * method as that will mask interrupts that should exit sleep mode. */
288         __disable_interrupt();
289         __DSB();
290         __ISB();
291 
292         /* If a context switch is pending or a task is waiting for the scheduler
293          * to be unsuspended then abandon the low power entry. */
294         if( eTaskConfirmSleepModeStatus() == eAbortSleep )
295         {
296             /* Re-enable interrupts - see comments above the __disable_interrupt()
297              * call above. */
298             __enable_interrupt();
299         }
300         else
301         {
302             /* Stop the SysTick momentarily.  The time the SysTick is stopped for
303              * is accounted for as best it can be, but using the tickless mode will
304              * inevitably result in some tiny drift of the time maintained by the
305              * kernel with respect to calendar time. */
306             portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
307 
308             /* Use the SysTick current-value register to determine the number of
309              * SysTick decrements remaining until the next tick interrupt.  If the
310              * current-value register is zero, then there are actually
311              * ulTimerCountsForOneTick decrements remaining, not zero, because the
312              * SysTick requests the interrupt when decrementing from 1 to 0. */
313             ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG;
314 
315             if( ulSysTickDecrementsLeft == 0 )
316             {
317                 ulSysTickDecrementsLeft = ulTimerCountsForOneTick;
318             }
319 
320             /* Calculate the reload value required to wait xExpectedIdleTime
321              * tick periods.  -1 is used because this code normally executes part
322              * way through the first tick period.  But if the SysTick IRQ is now
323              * pending, then clear the IRQ, suppressing the first tick, and correct
324              * the reload value to reflect that the second tick period is already
325              * underway.  The expected idle time is always at least two ticks. */
326             ulReloadValue = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
327 
328             if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 )
329             {
330                 portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT;
331                 ulReloadValue -= ulTimerCountsForOneTick;
332             }
333 
334             if( ulReloadValue > ulStoppedTimerCompensation )
335             {
336                 ulReloadValue -= ulStoppedTimerCompensation;
337             }
338 
339             /* Set the new reload value. */
340             portNVIC_SYSTICK_LOAD_REG = ulReloadValue;
341 
342             /* Clear the SysTick count flag and set the count value back to
343              * zero. */
344             portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
345 
346             /* Restart SysTick. */
347             portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
348 
349             /* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can
350              * set its parameter to 0 to indicate that its implementation contains
351              * its own wait for interrupt or wait for event instruction, and so wfi
352              * should not be executed again.  However, the original expected idle
353              * time variable must remain unmodified, so a copy is taken. */
354             xModifiableIdleTime = xExpectedIdleTime;
355             configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
356 
357             if( xModifiableIdleTime > 0 )
358             {
359                 __DSB();
360                 __WFI();
361                 __ISB();
362             }
363 
364             configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
365 
366             /* Re-enable interrupts to allow the interrupt that brought the MCU
367              * out of sleep mode to execute immediately.  See comments above
368              * the __disable_interrupt() call above. */
369             __enable_interrupt();
370             __DSB();
371             __ISB();
372 
373             /* Disable interrupts again because the clock is about to be stopped
374              * and interrupts that execute while the clock is stopped will increase
375              * any slippage between the time maintained by the RTOS and calendar
376              * time. */
377             __disable_interrupt();
378             __DSB();
379             __ISB();
380 
381             /* Disable the SysTick clock without reading the
382              * portNVIC_SYSTICK_CTRL_REG register to ensure the
383              * portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set.  Again,
384              * the time the SysTick is stopped for is accounted for as best it can
385              * be, but using the tickless mode will inevitably result in some tiny
386              * drift of the time maintained by the kernel with respect to calendar
387              * time*/
388             portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
389 
390             /* Determine whether the SysTick has already counted to zero. */
391             if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
392             {
393                 uint32_t ulCalculatedLoadValue;
394 
395                 /* The tick interrupt ended the sleep (or is now pending), and
396                  * a new tick period has started.  Reset portNVIC_SYSTICK_LOAD_REG
397                  * with whatever remains of the new tick period. */
398                 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );
399 
400                 /* Don't allow a tiny value, or values that have somehow
401                  * underflowed because the post sleep hook did something
402                  * that took too long or because the SysTick current-value register
403                  * is zero. */
404                 if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
405                 {
406                     ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
407                 }
408 
409                 portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;
410 
411                 /* As the pending tick will be processed as soon as this
412                  * function exits, the tick value maintained by the tick is stepped
413                  * forward by one less than the time spent waiting. */
414                 ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
415             }
416             else
417             {
418                 /* Something other than the tick interrupt ended the sleep. */
419 
420                 /* Use the SysTick current-value register to determine the
421                  * number of SysTick decrements remaining until the expected idle
422                  * time would have ended. */
423                 ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG;
424                 #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT )
425                 {
426                     /* If the SysTick is not using the core clock, the current-
427                      * value register might still be zero here.  In that case, the
428                      * SysTick didn't load from the reload register, and there are
429                      * ulReloadValue decrements remaining in the expected idle
430                      * time, not zero. */
431                     if( ulSysTickDecrementsLeft == 0 )
432                     {
433                         ulSysTickDecrementsLeft = ulReloadValue;
434                     }
435                 }
436                 #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */
437 
438                 /* Work out how long the sleep lasted rounded to complete tick
439                  * periods (not the ulReload value which accounted for part
440                  * ticks). */
441                 ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft;
442 
443                 /* How many complete tick periods passed while the processor
444                  * was waiting? */
445                 ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;
446 
447                 /* The reload value is set to whatever fraction of a single tick
448                  * period remains. */
449                 portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
450             }
451 
452             /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again,
453              * then set portNVIC_SYSTICK_LOAD_REG back to its standard value.  If
454              * the SysTick is not using the core clock, temporarily configure it to
455              * use the core clock.  This configuration forces the SysTick to load
456              * from portNVIC_SYSTICK_LOAD_REG immediately instead of at the next
457              * cycle of the other clock.  Then portNVIC_SYSTICK_LOAD_REG is ready
458              * to receive the standard value immediately. */
459             portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
460             portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
461             #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG == portNVIC_SYSTICK_CLK_BIT )
462             {
463                 portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
464             }
465             #else
466             {
467                 /* The temporary usage of the core clock has served its purpose,
468                  * as described above.  Resume usage of the other clock. */
469                 portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT;
470 
471                 if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
472                 {
473                     /* The partial tick period already ended.  Be sure the SysTick
474                      * counts it only once. */
475                     portNVIC_SYSTICK_CURRENT_VALUE_REG = 0;
476                 }
477 
478                 portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
479                 portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
480             }
481             #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */
482 
483             /* Step the tick to account for any tick periods that elapsed. */
484             vTaskStepTick( ulCompleteTickPeriods );
485 
486             /* Exit with interrupts enabled. */
487             __enable_interrupt();
488         }
489     }
490 
491 #endif /* configUSE_TICKLESS_IDLE */
492