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