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