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 /* Scheduler includes. */
34 #include "FreeRTOS.h"
35 #include "task.h"
36
37 /* Constants required to manipulate the NVIC. */
38 #define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) )
39 #define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) )
40 #define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) )
41 #define portNVIC_INT_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000ed04 ) )
42 #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) )
43 #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
44 #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
45 #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
46 #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
47 #define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
48 #define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL )
49 #define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL )
50 #define portMIN_INTERRUPT_PRIORITY ( 255UL )
51 #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL )
52 #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL )
53
54 /* Constants required to set up the initial stack. */
55 #define portINITIAL_XPSR ( 0x01000000 )
56
57 /* The systick is a 24-bit counter. */
58 #define portMAX_24_BIT_NUMBER ( 0xffffffUL )
59
60 /* A fiddle factor to estimate the number of SysTick counts that would have
61 * occurred while the SysTick counter is stopped during tickless idle
62 * calculations. */
63 #ifndef portMISSED_COUNTS_FACTOR
64 #define portMISSED_COUNTS_FACTOR ( 94UL )
65 #endif
66
67 /* Let the user override the default SysTick clock rate. If defined by the
68 * user, this symbol must equal the SysTick clock rate when the CLK bit is 0 in the
69 * configuration register. */
70 #ifndef configSYSTICK_CLOCK_HZ
71 #define configSYSTICK_CLOCK_HZ ( configCPU_CLOCK_HZ )
72 /* Ensure the SysTick is clocked at the same frequency as the core. */
73 #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( portNVIC_SYSTICK_CLK_BIT )
74 #else
75 /* Select the option to clock SysTick not at the same frequency as the core. */
76 #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 )
77 #endif
78
79 /* Let the user override the pre-loading of the initial LR with the address of
80 * prvTaskExitError() in case it messes up unwinding of the stack in the
81 * debugger. */
82 #ifdef configTASK_RETURN_ADDRESS
83 #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
84 #else
85 #define portTASK_RETURN_ADDRESS prvTaskExitError
86 #endif
87
88 /*
89 * Setup the timer to generate the tick interrupts. The implementation in this
90 * file is weak to allow application writers to change the timer used to
91 * generate the tick interrupt.
92 */
93 void vPortSetupTimerInterrupt( void );
94
95 /*
96 * Exception handlers.
97 */
98 void xPortPendSVHandler( void ) __attribute__( ( naked ) );
99 void xPortSysTickHandler( void );
100 void vPortSVCHandler( void );
101
102 /*
103 * Start first task is a separate function so it can be tested in isolation.
104 */
105 static void vPortStartFirstTask( void ) __attribute__( ( naked ) );
106
107 /*
108 * Used to catch tasks that attempt to return from their implementing function.
109 */
110 static void prvTaskExitError( void );
111
112 /*-----------------------------------------------------------*/
113
114 /* Each task maintains its own interrupt status in the critical nesting
115 * variable. */
116 static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
117
118 /*-----------------------------------------------------------*/
119
120 /*
121 * The number of SysTick increments that make up one tick period.
122 */
123 #if ( configUSE_TICKLESS_IDLE == 1 )
124 static uint32_t ulTimerCountsForOneTick = 0;
125 #endif /* configUSE_TICKLESS_IDLE */
126
127 /*
128 * The maximum number of tick periods that can be suppressed is limited by the
129 * 24 bit resolution of the SysTick timer.
130 */
131 #if ( configUSE_TICKLESS_IDLE == 1 )
132 static uint32_t xMaximumPossibleSuppressedTicks = 0;
133 #endif /* configUSE_TICKLESS_IDLE */
134
135 /*
136 * Compensate for the CPU cycles that pass while the SysTick is stopped (low
137 * power functionality only.
138 */
139 #if ( configUSE_TICKLESS_IDLE == 1 )
140 static uint32_t ulStoppedTimerCompensation = 0;
141 #endif /* configUSE_TICKLESS_IDLE */
142
143 /*-----------------------------------------------------------*/
144
145 /*
146 * See header file for description.
147 */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)148 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
149 TaskFunction_t pxCode,
150 void * pvParameters )
151 {
152 /* Simulate the stack frame as it would be created by a context switch
153 * interrupt. */
154 pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
155 *pxTopOfStack = portINITIAL_XPSR; /* xPSR */
156 pxTopOfStack--;
157 *pxTopOfStack = ( StackType_t ) pxCode; /* PC */
158 pxTopOfStack--;
159 *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */
160 pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
161 *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
162 pxTopOfStack -= 8; /* R11..R4. */
163
164 return pxTopOfStack;
165 }
166 /*-----------------------------------------------------------*/
167
prvTaskExitError(void)168 static void prvTaskExitError( void )
169 {
170 volatile uint32_t ulDummy = 0UL;
171
172 /* A function that implements a task must not exit or attempt to return to
173 * its caller as there is nothing to return to. If a task wants to exit it
174 * should instead call vTaskDelete( NULL ).
175 *
176 * Artificially force an assert() to be triggered if configASSERT() is
177 * defined, then stop here so application writers can catch the error. */
178 configASSERT( uxCriticalNesting == ~0UL );
179 portDISABLE_INTERRUPTS();
180
181 while( ulDummy == 0 )
182 {
183 /* This file calls prvTaskExitError() after the scheduler has been
184 * started to remove a compiler warning about the function being defined
185 * but never called. ulDummy is used purely to quieten other warnings
186 * about code appearing after this function is called - making ulDummy
187 * volatile makes the compiler think the function could return and
188 * therefore not output an 'unreachable code' warning for code that appears
189 * after it. */
190 }
191 }
192 /*-----------------------------------------------------------*/
193
vPortSVCHandler(void)194 void vPortSVCHandler( void )
195 {
196 /* This function is no longer used, but retained for backward
197 * compatibility. */
198 }
199 /*-----------------------------------------------------------*/
200
vPortStartFirstTask(void)201 void vPortStartFirstTask( void )
202 {
203 /* The MSP stack is not reset as, unlike on M3/4 parts, there is no vector
204 * table offset register that can be used to locate the initial stack value.
205 * Not all M0 parts have the application vector table at address 0. */
206 __asm volatile (
207 " .syntax unified \n"
208 " ldr r2, pxCurrentTCBConst2 \n"/* Obtain location of pxCurrentTCB. */
209 " ldr r3, [r2] \n"
210 " ldr r0, [r3] \n"/* The first item in pxCurrentTCB is the task top of stack. */
211 " adds r0, #32 \n"/* Discard everything up to r0. */
212 " msr psp, r0 \n"/* This is now the new top of stack to use in the task. */
213 " movs r0, #2 \n"/* Switch to the psp stack. */
214 " msr CONTROL, r0 \n"
215 " isb \n"
216 " pop {r0-r5} \n"/* Pop the registers that are saved automatically. */
217 " mov lr, r5 \n"/* lr is now in r5. */
218 " pop {r3} \n"/* Return address is now in r3. */
219 " pop {r2} \n"/* Pop and discard XPSR. */
220 " cpsie i \n"/* The first task has its context and interrupts can be enabled. */
221 " bx r3 \n"/* Finally, jump to the user defined task code. */
222 " \n"
223 " .align 4 \n"
224 "pxCurrentTCBConst2: .word pxCurrentTCB "
225 );
226 }
227 /*-----------------------------------------------------------*/
228
229 /*
230 * See header file for description.
231 */
xPortStartScheduler(void)232 BaseType_t xPortStartScheduler( void )
233 {
234 /* Make PendSV, CallSV and SysTick the same priority as the kernel. */
235 portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
236 portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
237
238 /* Start the timer that generates the tick ISR. Interrupts are disabled
239 * here already. */
240 vPortSetupTimerInterrupt();
241
242 /* Initialise the critical nesting count ready for the first task. */
243 uxCriticalNesting = 0;
244
245 /* Start the first task. */
246 vPortStartFirstTask();
247
248 /* Should never get here as the tasks will now be executing! Call the task
249 * exit error function to prevent compiler warnings about a static function
250 * not being called in the case that the application writer overrides this
251 * functionality by defining configTASK_RETURN_ADDRESS. Call
252 * vTaskSwitchContext() so link time optimisation does not remove the
253 * symbol. */
254 vTaskSwitchContext();
255 prvTaskExitError();
256
257 /* Should not get here! */
258 return 0;
259 }
260 /*-----------------------------------------------------------*/
261
vPortEndScheduler(void)262 void vPortEndScheduler( void )
263 {
264 /* Not implemented in ports where there is nothing to return to.
265 * Artificially force an assert. */
266 configASSERT( uxCriticalNesting == 1000UL );
267 }
268 /*-----------------------------------------------------------*/
269
vPortYield(void)270 void vPortYield( void )
271 {
272 /* Set a PendSV to request a context switch. */
273 portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
274
275 /* Barriers are normally not required but do ensure the code is completely
276 * within the specified behaviour for the architecture. */
277 __asm volatile ( "dsb" ::: "memory" );
278 __asm volatile ( "isb" );
279 }
280 /*-----------------------------------------------------------*/
281
vPortEnterCritical(void)282 void vPortEnterCritical( void )
283 {
284 portDISABLE_INTERRUPTS();
285 uxCriticalNesting++;
286 __asm volatile ( "dsb" ::: "memory" );
287 __asm volatile ( "isb" );
288 }
289 /*-----------------------------------------------------------*/
290
vPortExitCritical(void)291 void vPortExitCritical( void )
292 {
293 configASSERT( uxCriticalNesting );
294 uxCriticalNesting--;
295
296 if( uxCriticalNesting == 0 )
297 {
298 portENABLE_INTERRUPTS();
299 }
300 }
301 /*-----------------------------------------------------------*/
302
ulSetInterruptMaskFromISR(void)303 uint32_t ulSetInterruptMaskFromISR( void )
304 {
305 __asm volatile (
306 " mrs r0, PRIMASK \n"
307 " cpsid i \n"
308 " bx lr "
309 ::: "memory"
310 );
311 }
312 /*-----------------------------------------------------------*/
313
vClearInterruptMaskFromISR(uint32_t ulMask)314 void vClearInterruptMaskFromISR( __attribute__( ( unused ) ) uint32_t ulMask )
315 {
316 __asm volatile (
317 " msr PRIMASK, r0 \n"
318 " bx lr "
319 ::: "memory"
320 );
321 }
322 /*-----------------------------------------------------------*/
323
xPortPendSVHandler(void)324 void xPortPendSVHandler( void )
325 {
326 /* This is a naked function. */
327
328 __asm volatile
329 (
330 " .syntax unified \n"
331 " mrs r0, psp \n"
332 " \n"
333 " ldr r3, pxCurrentTCBConst \n"/* Get the location of the current TCB. */
334 " ldr r2, [r3] \n"
335 " \n"
336 " subs r0, r0, #32 \n"/* Make space for the remaining low registers. */
337 " str r0, [r2] \n"/* Save the new top of stack. */
338 " stmia r0!, {r4-r7} \n"/* Store the low registers that are not saved automatically. */
339 " mov r4, r8 \n"/* Store the high registers. */
340 " mov r5, r9 \n"
341 " mov r6, r10 \n"
342 " mov r7, r11 \n"
343 " stmia r0!, {r4-r7} \n"
344 " \n"
345 " push {r3, r14} \n"
346 " cpsid i \n"
347 " bl vTaskSwitchContext \n"
348 " cpsie i \n"
349 " pop {r2, r3} \n"/* lr goes in r3. r2 now holds tcb pointer. */
350 " \n"
351 " ldr r1, [r2] \n"
352 " ldr r0, [r1] \n"/* The first item in pxCurrentTCB is the task top of stack. */
353 " adds r0, r0, #16 \n"/* Move to the high registers. */
354 " ldmia r0!, {r4-r7} \n"/* Pop the high registers. */
355 " mov r8, r4 \n"
356 " mov r9, r5 \n"
357 " mov r10, r6 \n"
358 " mov r11, r7 \n"
359 " \n"
360 " msr psp, r0 \n"/* Remember the new top of stack for the task. */
361 " \n"
362 " subs r0, r0, #32 \n"/* Go back for the low registers that are not automatically restored. */
363 " ldmia r0!, {r4-r7} \n"/* Pop low registers. */
364 " \n"
365 " bx r3 \n"
366 " \n"
367 " .align 4 \n"
368 "pxCurrentTCBConst: .word pxCurrentTCB "
369 );
370 }
371 /*-----------------------------------------------------------*/
372
xPortSysTickHandler(void)373 void xPortSysTickHandler( void )
374 {
375 uint32_t ulPreviousMask;
376
377 ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();
378 {
379 /* Increment the RTOS tick. */
380 if( xTaskIncrementTick() != pdFALSE )
381 {
382 /* Pend a context switch. */
383 portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
384 }
385 }
386 portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );
387 }
388 /*-----------------------------------------------------------*/
389
390 /*
391 * Setup the systick timer to generate the tick interrupts at the required
392 * frequency.
393 */
vPortSetupTimerInterrupt(void)394 __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void )
395 {
396 /* Calculate the constants required to configure the tick interrupt. */
397 #if ( configUSE_TICKLESS_IDLE == 1 )
398 {
399 ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
400 xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
401 ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
402 }
403 #endif /* configUSE_TICKLESS_IDLE */
404
405 /* Stop and reset the SysTick. */
406 portNVIC_SYSTICK_CTRL_REG = 0UL;
407 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
408
409 /* Configure SysTick to interrupt at the requested rate. */
410 portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
411 portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
412 }
413 /*-----------------------------------------------------------*/
414
415 #if ( configUSE_TICKLESS_IDLE == 1 )
416
vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)417 __attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
418 {
419 uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft;
420 TickType_t xModifiableIdleTime;
421
422 /* Make sure the SysTick reload value does not overflow the counter. */
423 if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
424 {
425 xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
426 }
427
428 /* Enter a critical section but don't use the taskENTER_CRITICAL()
429 * method as that will mask interrupts that should exit sleep mode. */
430 __asm volatile ( "cpsid i" ::: "memory" );
431 __asm volatile ( "dsb" );
432 __asm volatile ( "isb" );
433
434 /* If a context switch is pending or a task is waiting for the scheduler
435 * to be unsuspended then abandon the low power entry. */
436 if( eTaskConfirmSleepModeStatus() == eAbortSleep )
437 {
438 /* Re-enable interrupts - see comments above the cpsid instruction
439 * above. */
440 __asm volatile ( "cpsie i" ::: "memory" );
441 }
442 else
443 {
444 /* Stop the SysTick momentarily. The time the SysTick is stopped for
445 * is accounted for as best it can be, but using the tickless mode will
446 * inevitably result in some tiny drift of the time maintained by the
447 * kernel with respect to calendar time. */
448 portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
449
450 /* Use the SysTick current-value register to determine the number of
451 * SysTick decrements remaining until the next tick interrupt. If the
452 * current-value register is zero, then there are actually
453 * ulTimerCountsForOneTick decrements remaining, not zero, because the
454 * SysTick requests the interrupt when decrementing from 1 to 0. */
455 ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG;
456
457 if( ulSysTickDecrementsLeft == 0 )
458 {
459 ulSysTickDecrementsLeft = ulTimerCountsForOneTick;
460 }
461
462 /* Calculate the reload value required to wait xExpectedIdleTime
463 * tick periods. -1 is used because this code normally executes part
464 * way through the first tick period. But if the SysTick IRQ is now
465 * pending, then clear the IRQ, suppressing the first tick, and correct
466 * the reload value to reflect that the second tick period is already
467 * underway. The expected idle time is always at least two ticks. */
468 ulReloadValue = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
469
470 if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 )
471 {
472 portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT;
473 ulReloadValue -= ulTimerCountsForOneTick;
474 }
475
476 if( ulReloadValue > ulStoppedTimerCompensation )
477 {
478 ulReloadValue -= ulStoppedTimerCompensation;
479 }
480
481 /* Set the new reload value. */
482 portNVIC_SYSTICK_LOAD_REG = ulReloadValue;
483
484 /* Clear the SysTick count flag and set the count value back to
485 * zero. */
486 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
487
488 /* Restart SysTick. */
489 portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
490
491 /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can
492 * set its parameter to 0 to indicate that its implementation contains
493 * its own wait for interrupt or wait for event instruction, and so wfi
494 * should not be executed again. However, the original expected idle
495 * time variable must remain unmodified, so a copy is taken. */
496 xModifiableIdleTime = xExpectedIdleTime;
497 configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
498
499 if( xModifiableIdleTime > 0 )
500 {
501 __asm volatile ( "dsb" ::: "memory" );
502 __asm volatile ( "wfi" );
503 __asm volatile ( "isb" );
504 }
505
506 configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
507
508 /* Re-enable interrupts to allow the interrupt that brought the MCU
509 * out of sleep mode to execute immediately. See comments above
510 * the cpsid instruction above. */
511 __asm volatile ( "cpsie i" ::: "memory" );
512 __asm volatile ( "dsb" );
513 __asm volatile ( "isb" );
514
515 /* Disable interrupts again because the clock is about to be stopped
516 * and interrupts that execute while the clock is stopped will increase
517 * any slippage between the time maintained by the RTOS and calendar
518 * time. */
519 __asm volatile ( "cpsid i" ::: "memory" );
520 __asm volatile ( "dsb" );
521 __asm volatile ( "isb" );
522
523 /* Disable the SysTick clock without reading the
524 * portNVIC_SYSTICK_CTRL_REG register to ensure the
525 * portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again,
526 * the time the SysTick is stopped for is accounted for as best it can
527 * be, but using the tickless mode will inevitably result in some tiny
528 * drift of the time maintained by the kernel with respect to calendar
529 * time*/
530 portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
531
532 /* Determine whether the SysTick has already counted to zero. */
533 if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
534 {
535 uint32_t ulCalculatedLoadValue;
536
537 /* The tick interrupt ended the sleep (or is now pending), and
538 * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG
539 * with whatever remains of the new tick period. */
540 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );
541
542 /* Don't allow a tiny value, or values that have somehow
543 * underflowed because the post sleep hook did something
544 * that took too long or because the SysTick current-value register
545 * is zero. */
546 if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
547 {
548 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
549 }
550
551 portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;
552
553 /* As the pending tick will be processed as soon as this
554 * function exits, the tick value maintained by the tick is stepped
555 * forward by one less than the time spent waiting. */
556 ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
557 }
558 else
559 {
560 /* Something other than the tick interrupt ended the sleep. */
561
562 /* Use the SysTick current-value register to determine the
563 * number of SysTick decrements remaining until the expected idle
564 * time would have ended. */
565 ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG;
566 #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT )
567 {
568 /* If the SysTick is not using the core clock, the current-
569 * value register might still be zero here. In that case, the
570 * SysTick didn't load from the reload register, and there are
571 * ulReloadValue decrements remaining in the expected idle
572 * time, not zero. */
573 if( ulSysTickDecrementsLeft == 0 )
574 {
575 ulSysTickDecrementsLeft = ulReloadValue;
576 }
577 }
578 #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */
579
580 /* Work out how long the sleep lasted rounded to complete tick
581 * periods (not the ulReload value which accounted for part
582 * ticks). */
583 ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft;
584
585 /* How many complete tick periods passed while the processor
586 * was waiting? */
587 ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;
588
589 /* The reload value is set to whatever fraction of a single tick
590 * period remains. */
591 portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
592 }
593
594 /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again,
595 * then set portNVIC_SYSTICK_LOAD_REG back to its standard value. If
596 * the SysTick is not using the core clock, temporarily configure it to
597 * use the core clock. This configuration forces the SysTick to load
598 * from portNVIC_SYSTICK_LOAD_REG immediately instead of at the next
599 * cycle of the other clock. Then portNVIC_SYSTICK_LOAD_REG is ready
600 * to receive the standard value immediately. */
601 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
602 portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
603 #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG == portNVIC_SYSTICK_CLK_BIT )
604 {
605 portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
606 }
607 #else
608 {
609 /* The temporary usage of the core clock has served its purpose,
610 * as described above. Resume usage of the other clock. */
611 portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT;
612
613 if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
614 {
615 /* The partial tick period already ended. Be sure the SysTick
616 * counts it only once. */
617 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0;
618 }
619
620 portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
621 portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
622 }
623 #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */
624
625 /* Step the tick to account for any tick periods that elapsed. */
626 vTaskStepTick( ulCompleteTickPeriods );
627
628 /* Exit with interrupts enabled. */
629 __asm volatile ( "cpsie i" ::: "memory" );
630 }
631 }
632
633 #endif /* configUSE_TICKLESS_IDLE */
634