1 /*
2  * FreeRTOS Kernel V11.1.0
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 RX100 port.
31  *----------------------------------------------------------*/
32 
33 /* Standard C includes. */
34 #include "limits.h"
35 
36 /* Scheduler includes. */
37 #include "FreeRTOS.h"
38 #include "task.h"
39 
40 /* Library includes. */
41 #include "string.h"
42 
43 /* Hardware specifics. */
44 #include "iodefine.h"
45 
46 /*-----------------------------------------------------------*/
47 
48 /* Tasks should start with interrupts enabled and in Supervisor mode, therefore
49 PSW is set with U and I set, and PM and IPL clear. */
50 #define portINITIAL_PSW     ( ( StackType_t ) 0x00030000 )
51 
52 /* The peripheral clock is divided by this value before being supplying the
53 CMT. */
54 #if ( configUSE_TICKLESS_IDLE == 0 )
55     /* If tickless idle is not used then the divisor can be fixed. */
56     #define portCLOCK_DIVISOR   8UL
57 #elif ( configPERIPHERAL_CLOCK_HZ >= 12000000 )
58     #define portCLOCK_DIVISOR   512UL
59 #elif ( configPERIPHERAL_CLOCK_HZ >= 6000000 )
60     #define portCLOCK_DIVISOR   128UL
61 #elif ( configPERIPHERAL_CLOCK_HZ >= 1000000 )
62     #define portCLOCK_DIVISOR   32UL
63 #else
64     #define portCLOCK_DIVISOR   8UL
65 #endif
66 
67 
68 /* Keys required to lock and unlock access to certain system registers
69 respectively. */
70 #define portUNLOCK_KEY      0xA50B
71 #define portLOCK_KEY        0xA500
72 
73 /*-----------------------------------------------------------*/
74 
75 /* The following lines are to ensure vSoftwareInterruptEntry can be referenced,
76  and therefore installed in the vector table, when the FreeRTOS code is built
77 as a library. */
78 extern BaseType_t vSoftwareInterruptEntry;
79 const BaseType_t * p_vSoftwareInterruptEntry = &vSoftwareInterruptEntry;
80 
81 /*-----------------------------------------------------------*/
82 
83 /*
84  * Function to start the first task executing - written in asm code as direct
85  * access to registers is required.
86  */
87 static void prvStartFirstTask( void );
88 
89 /*
90  * Software interrupt handler.  Performs the actual context switch (saving and
91  * restoring of registers).  Written in asm code as direct register access is
92  * required.
93  */
94 static void prvYieldHandler( void );
95 
96 /*
97  * The entry point for the software interrupt handler.  This is the function
98  * that calls the inline asm function prvYieldHandler().  It is installed in
99  * the vector table, but the code that installs it is in prvYieldHandler rather
100  * than using a #pragma.
101  */
102 void vSoftwareInterruptISR( void );
103 
104 /*
105  * Sets up the periodic ISR used for the RTOS tick using the CMT.
106  * The application writer can define configSETUP_TICK_INTERRUPT() (in
107  * FreeRTOSConfig.h) such that their own tick interrupt configuration is used
108  * in place of prvSetupTimerInterrupt().
109  */
110 static void prvSetupTimerInterrupt( void );
111 #ifndef configSETUP_TICK_INTERRUPT
112     /* The user has not provided their own tick interrupt configuration so use
113     the definition in this file (which uses the interval timer). */
114     #define configSETUP_TICK_INTERRUPT() prvSetupTimerInterrupt()
115 #endif /* configSETUP_TICK_INTERRUPT */
116 
117 /*
118  * Called after the sleep mode registers have been configured, prvSleep()
119  * executes the pre and post sleep macros, and actually calls the wait
120  * instruction.
121  */
122 #if configUSE_TICKLESS_IDLE == 1
123     static void prvSleep( TickType_t xExpectedIdleTime );
124 #endif /* configUSE_TICKLESS_IDLE */
125 
126 /*-----------------------------------------------------------*/
127 
128 /* These is accessed by the inline assembler functions. */
129 extern void *pxCurrentTCB;
130 extern void vTaskSwitchContext( void );
131 
132 /*-----------------------------------------------------------*/
133 
134 /* Calculate how many clock increments make up a single tick period. */
135 static const uint32_t ulMatchValueForOneTick = ( ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) / configTICK_RATE_HZ );
136 
137 #if configUSE_TICKLESS_IDLE == 1
138 
139     /* Holds the maximum number of ticks that can be suppressed - which is
140     basically how far into the future an interrupt can be generated. Set
141     during initialisation.  This is the maximum possible value that the
142     compare match register can hold divided by ulMatchValueForOneTick. */
143     static const TickType_t xMaximumPossibleSuppressedTicks = USHRT_MAX / ( ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) / configTICK_RATE_HZ );
144 
145     /* Flag set from the tick interrupt to allow the sleep processing to know if
146     sleep mode was exited because of a tick interrupt, or an interrupt
147     generated by something else. */
148     static volatile uint32_t ulTickFlag = pdFALSE;
149 
150     /* The CMT counter is stopped temporarily each time it is re-programmed.
151     The following constant offsets the CMT counter match value by the number of
152     CMT counts that would typically be missed while the counter was stopped to
153     compensate for the lost time.  The large difference between the divided CMT
154     clock and the CPU clock means it is likely ulStoppedTimerCompensation will
155     equal zero - and be optimised away. */
156     static const uint32_t ulStoppedTimerCompensation = 100UL / ( configCPU_CLOCK_HZ / ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) );
157 
158 #endif
159 
160 /*-----------------------------------------------------------*/
161 
162 /*
163  * See header file for description.
164  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)165 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
166 {
167     /* Offset to end up on 8 byte boundary. */
168     pxTopOfStack--;
169 
170     /* R0 is not included as it is the stack pointer. */
171     *pxTopOfStack = 0x00;
172     pxTopOfStack--;
173     *pxTopOfStack = 0x00;
174     pxTopOfStack--;
175     *pxTopOfStack = portINITIAL_PSW;
176     pxTopOfStack--;
177     *pxTopOfStack = ( StackType_t ) pxCode;
178 
179     /* When debugging it can be useful if every register is set to a known
180     value.  Otherwise code space can be saved by just setting the registers
181     that need to be set. */
182     #ifdef USE_FULL_REGISTER_INITIALISATION
183     {
184         pxTopOfStack--;
185         *pxTopOfStack = 0x12345678; /* r15. */
186         pxTopOfStack--;
187         *pxTopOfStack = 0xaaaabbbb;
188         pxTopOfStack--;
189         *pxTopOfStack = 0xdddddddd;
190         pxTopOfStack--;
191         *pxTopOfStack = 0xcccccccc;
192         pxTopOfStack--;
193         *pxTopOfStack = 0xbbbbbbbb;
194         pxTopOfStack--;
195         *pxTopOfStack = 0xaaaaaaaa;
196         pxTopOfStack--;
197         *pxTopOfStack = 0x99999999;
198         pxTopOfStack--;
199         *pxTopOfStack = 0x88888888;
200         pxTopOfStack--;
201         *pxTopOfStack = 0x77777777;
202         pxTopOfStack--;
203         *pxTopOfStack = 0x66666666;
204         pxTopOfStack--;
205         *pxTopOfStack = 0x55555555;
206         pxTopOfStack--;
207         *pxTopOfStack = 0x44444444;
208         pxTopOfStack--;
209         *pxTopOfStack = 0x33333333;
210         pxTopOfStack--;
211         *pxTopOfStack = 0x22222222;
212         pxTopOfStack--;
213     }
214     #else
215     {
216         /* Leave space for the registers that will get popped from the stack
217         when the task first starts executing. */
218         pxTopOfStack -= 15;
219     }
220     #endif
221 
222     *pxTopOfStack = ( StackType_t ) pvParameters; /* R1 */
223     pxTopOfStack--;
224     *pxTopOfStack = 0x12345678; /* Accumulator. */
225     pxTopOfStack--;
226     *pxTopOfStack = 0x87654321; /* Accumulator. */
227 
228     return pxTopOfStack;
229 }
230 /*-----------------------------------------------------------*/
231 
xPortStartScheduler(void)232 BaseType_t xPortStartScheduler( void )
233 {
234     /* Use pxCurrentTCB just so it does not get optimised away. */
235     if( pxCurrentTCB != NULL )
236     {
237         /* Call an application function to set up the timer that will generate
238         the tick interrupt.  This way the application can decide which
239         peripheral to use.  If tickless mode is used then the default
240         implementation defined in this file (which uses CMT0) should not be
241         overridden. */
242         configSETUP_TICK_INTERRUPT();
243 
244         /* Enable the software interrupt. */
245         _IEN( _ICU_SWINT ) = 1;
246 
247         /* Ensure the software interrupt is clear. */
248         _IR( _ICU_SWINT ) = 0;
249 
250         /* Ensure the software interrupt is set to the kernel priority. */
251         _IPR( _ICU_SWINT ) = configKERNEL_INTERRUPT_PRIORITY;
252 
253         /* Start the first task. */
254         prvStartFirstTask();
255     }
256 
257     /* Execution should not reach here as the tasks are now running!
258     prvSetupTimerInterrupt() is called here to prevent the compiler outputting
259     a warning about a statically declared function not being referenced in the
260     case that the application writer has provided their own tick interrupt
261     configuration routine (and defined configSETUP_TICK_INTERRUPT() such that
262     their own routine will be called in place of prvSetupTimerInterrupt()). */
263     prvSetupTimerInterrupt();
264 
265     /* Just to make sure the function is not optimised away. */
266     ( void ) vSoftwareInterruptISR();
267 
268     /* Should not get here. */
269     return pdFAIL;
270 }
271 /*-----------------------------------------------------------*/
272 
273 #pragma inline_asm prvStartFirstTask
prvStartFirstTask(void)274 static void prvStartFirstTask( void )
275 {
276     /* When starting the scheduler there is nothing that needs moving to the
277     interrupt stack because the function is not called from an interrupt.
278     Just ensure the current stack is the user stack. */
279     SETPSW  U
280 
281     /* Obtain the location of the stack associated with which ever task
282     pxCurrentTCB is currently pointing to. */
283     MOV.L   #_pxCurrentTCB, R15
284     MOV.L   [R15], R15
285     MOV.L   [R15], R0
286 
287     /* Restore the registers from the stack of the task pointed to by
288     pxCurrentTCB. */
289     POP     R15
290     MVTACLO R15         /* Accumulator low 32 bits. */
291     POP     R15
292     MVTACHI R15         /* Accumulator high 32 bits. */
293     POPM    R1-R15      /* R1 to R15 - R0 is not included as it is the SP. */
294     RTE                 /* This pops the remaining registers. */
295     NOP
296     NOP
297 }
298 /*-----------------------------------------------------------*/
299 
300 #pragma interrupt ( prvTickISR( vect = _VECT( configTICK_VECTOR ), enable ) )
prvTickISR(void)301 void prvTickISR( void )
302 {
303     /* Increment the tick, and perform any processing the new tick value
304     necessitates. */
305     set_ipl( configMAX_SYSCALL_INTERRUPT_PRIORITY );
306     {
307         if( xTaskIncrementTick() != pdFALSE )
308         {
309             taskYIELD();
310         }
311     }
312     set_ipl( configKERNEL_INTERRUPT_PRIORITY );
313 
314     #if configUSE_TICKLESS_IDLE == 1
315     {
316         /* The CPU woke because of a tick. */
317         ulTickFlag = pdTRUE;
318 
319         /* If this is the first tick since exiting tickless mode then the CMT
320         compare match value needs resetting. */
321         CMT0.CMCOR = ( uint16_t ) ulMatchValueForOneTick;
322     }
323     #endif
324 }
325 /*-----------------------------------------------------------*/
326 
vSoftwareInterruptISR(void)327 void vSoftwareInterruptISR( void )
328 {
329     prvYieldHandler();
330 }
331 /*-----------------------------------------------------------*/
332 
333 #pragma inline_asm prvYieldHandler
prvYieldHandler(void)334 static void prvYieldHandler( void )
335 {
336     /* Re-enable interrupts. */
337     SETPSW  I
338 
339     /* Move the data that was automatically pushed onto the interrupt stack
340     when the interrupt occurred from the interrupt stack to the user stack.
341 
342     R15 is saved before it is clobbered. */
343     PUSH.L  R15
344 
345     /* Read the user stack pointer. */
346     MVFC    USP, R15
347 
348     /* Move the address down to the data being moved. */
349     SUB     #12, R15
350     MVTC    R15, USP
351 
352     /* Copy the data across. */
353     MOV.L   [ R0 ], [ R15 ] ; R15
354     MOV.L   4[ R0 ], 4[ R15 ]  ; PC
355     MOV.L   8[ R0 ], 8[ R15 ]  ; PSW
356 
357     /* Move the interrupt stack pointer to its new correct position. */
358     ADD #12, R0
359 
360     /* All the rest of the registers are saved directly to the user stack. */
361     SETPSW  U
362 
363     /* Save the rest of the general registers (R15 has been saved already). */
364     PUSHM   R1-R14
365 
366     /* Save the accumulator. */
367     MVFACHI R15
368     PUSH.L  R15
369     MVFACMI R15 ; Middle order word.
370     SHLL    #16, R15 ; Shifted left as it is restored to the low order word.
371     PUSH.L  R15
372 
373     /* Save the stack pointer to the TCB. */
374     MOV.L   #_pxCurrentTCB, R15
375     MOV.L   [ R15 ], R15
376     MOV.L   R0, [ R15 ]
377 
378     /* Ensure the interrupt mask is set to the syscall priority while the
379     kernel structures are being accessed. */
380     MVTIPL  #configMAX_SYSCALL_INTERRUPT_PRIORITY
381 
382     /* Select the next task to run. */
383     BSR.A   _vTaskSwitchContext
384 
385     /* Reset the interrupt mask as no more data structure access is
386     required. */
387     MVTIPL  #configKERNEL_INTERRUPT_PRIORITY
388 
389     /* Load the stack pointer of the task that is now selected as the Running
390     state task from its TCB. */
391     MOV.L   #_pxCurrentTCB,R15
392     MOV.L   [ R15 ], R15
393     MOV.L   [ R15 ], R0
394 
395     /* Restore the context of the new task.  The PSW (Program Status Word) and
396     PC will be popped by the RTE instruction. */
397     POP     R15
398     MVTACLO R15
399     POP     R15
400     MVTACHI R15
401     POPM    R1-R15
402     RTE
403     NOP
404     NOP
405 }
406 /*-----------------------------------------------------------*/
407 
vPortEndScheduler(void)408 void vPortEndScheduler( void )
409 {
410     /* Not implemented in ports where there is nothing to return to.
411     Artificially force an assert. */
412     configASSERT( pxCurrentTCB == NULL );
413 
414     /* The following line is just to prevent the symbol getting optimised away. */
415     ( void ) vTaskSwitchContext();
416 }
417 /*-----------------------------------------------------------*/
418 
prvSetupTimerInterrupt(void)419 static void prvSetupTimerInterrupt( void )
420 {
421     /* Unlock. */
422     SYSTEM.PRCR.WORD = portUNLOCK_KEY;
423 
424     /* Enable CMT0. */
425     MSTP( CMT0 ) = 0;
426 
427     /* Lock again. */
428     SYSTEM.PRCR.WORD = portLOCK_KEY;
429 
430     /* Interrupt on compare match. */
431     CMT0.CMCR.BIT.CMIE = 1;
432 
433     /* Set the compare match value. */
434     CMT0.CMCOR = ( uint16_t ) ulMatchValueForOneTick;
435 
436     /* Divide the PCLK. */
437     #if portCLOCK_DIVISOR == 512
438     {
439         CMT0.CMCR.BIT.CKS = 3;
440     }
441     #elif portCLOCK_DIVISOR == 128
442     {
443         CMT0.CMCR.BIT.CKS = 2;
444     }
445     #elif portCLOCK_DIVISOR == 32
446     {
447         CMT0.CMCR.BIT.CKS = 1;
448     }
449     #elif portCLOCK_DIVISOR == 8
450     {
451         CMT0.CMCR.BIT.CKS = 0;
452     }
453     #else
454     {
455         #error Invalid portCLOCK_DIVISOR setting
456     }
457     #endif
458 
459 
460     /* Enable the interrupt... */
461     _IEN( _CMT0_CMI0 ) = 1;
462 
463     /* ...and set its priority to the application defined kernel priority. */
464     _IPR( _CMT0_CMI0 ) = configKERNEL_INTERRUPT_PRIORITY;
465 
466     /* Start the timer. */
467     CMT.CMSTR0.BIT.STR0 = 1;
468 }
469 /*-----------------------------------------------------------*/
470 
471 #if configUSE_TICKLESS_IDLE == 1
472 
prvSleep(TickType_t xExpectedIdleTime)473     static void prvSleep( TickType_t xExpectedIdleTime )
474     {
475         /* Allow the application to define some pre-sleep processing. */
476         configPRE_SLEEP_PROCESSING( xExpectedIdleTime );
477 
478         /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()
479         means the application defined code has already executed the WAIT
480         instruction. */
481         if( xExpectedIdleTime > 0 )
482         {
483             wait();
484         }
485 
486         /* Allow the application to define some post sleep processing. */
487         configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
488     }
489 
490 #endif /* configUSE_TICKLESS_IDLE */
491 /*-----------------------------------------------------------*/
492 
493 #if configUSE_TICKLESS_IDLE == 1
494 
vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)495     void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
496     {
497     uint32_t ulMatchValue, ulCompleteTickPeriods, ulCurrentCount;
498     eSleepModeStatus eSleepAction;
499 
500         /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */
501 
502         /* Make sure the CMT reload value does not overflow the counter. */
503         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
504         {
505             xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
506         }
507 
508         /* Calculate the reload value required to wait xExpectedIdleTime tick
509         periods. */
510         ulMatchValue = ulMatchValueForOneTick * xExpectedIdleTime;
511         if( ulMatchValue > ulStoppedTimerCompensation )
512         {
513             /* Compensate for the fact that the CMT is going to be stopped
514             momentarily. */
515             ulMatchValue -= ulStoppedTimerCompensation;
516         }
517 
518         /* Stop the CMT momentarily.  The time the CMT is stopped for is
519         accounted for as best it can be, but using the tickless mode will
520         inevitably result in some tiny drift of the time maintained by the
521         kernel with respect to calendar time. */
522         CMT.CMSTR0.BIT.STR0 = 0;
523         while( CMT.CMSTR0.BIT.STR0 == 1 )
524         {
525             /* Nothing to do here. */
526         }
527 
528         /* Critical section using the global interrupt bit as the i bit is
529         automatically reset by the WAIT instruction. */
530         clrpsw_i();
531 
532         /* The tick flag is set to false before sleeping.  If it is true when
533         sleep mode is exited then sleep mode was probably exited because the
534         tick was suppressed for the entire xExpectedIdleTime period. */
535         ulTickFlag = pdFALSE;
536 
537         /* If a context switch is pending then abandon the low power entry as
538         the context switch might have been pended by an external interrupt that
539         requires processing. */
540         eSleepAction = eTaskConfirmSleepModeStatus();
541         if( eSleepAction == eAbortSleep )
542         {
543             /* Restart tick. */
544             CMT.CMSTR0.BIT.STR0 = 1;
545             setpsw_i();
546         }
547         else if( eSleepAction == eNoTasksWaitingTimeout )
548         {
549             /* Protection off. */
550             SYSTEM.PRCR.WORD = portUNLOCK_KEY;
551 
552             /* Ready for software standby with all clocks stopped. */
553             SYSTEM.SBYCR.BIT.SSBY = 1;
554 
555             /* Protection on. */
556             SYSTEM.PRCR.WORD = portLOCK_KEY;
557 
558             /* Sleep until something happens.  Calling prvSleep() will
559             automatically reset the i bit in the PSW. */
560             prvSleep( xExpectedIdleTime );
561 
562             /* Restart the CMT. */
563             CMT.CMSTR0.BIT.STR0 = 1;
564         }
565         else
566         {
567             /* Protection off. */
568             SYSTEM.PRCR.WORD = portUNLOCK_KEY;
569 
570             /* Ready for deep sleep mode. */
571             SYSTEM.MSTPCRC.BIT.DSLPE = 1;
572             SYSTEM.MSTPCRA.BIT.MSTPA28 = 1;
573             SYSTEM.SBYCR.BIT.SSBY = 0;
574 
575             /* Protection on. */
576             SYSTEM.PRCR.WORD = portLOCK_KEY;
577 
578             /* Adjust the match value to take into account that the current
579             time slice is already partially complete. */
580             ulMatchValue -= ( uint32_t ) CMT0.CMCNT;
581             CMT0.CMCOR = ( uint16_t ) ulMatchValue;
582 
583             /* Restart the CMT to count up to the new match value. */
584             CMT0.CMCNT = 0;
585             CMT.CMSTR0.BIT.STR0 = 1;
586 
587             /* Sleep until something happens.  Calling prvSleep() will
588             automatically reset the i bit in the PSW. */
589             prvSleep( xExpectedIdleTime );
590 
591             /* Stop CMT.  Again, the time the SysTick is stopped for is
592             accounted for as best it can be, but using the tickless mode will
593             inevitably result in some tiny drift of the time maintained by the
594             kernel with respect to calendar time. */
595             CMT.CMSTR0.BIT.STR0 = 0;
596             while( CMT.CMSTR0.BIT.STR0 == 1 )
597             {
598                 /* Nothing to do here. */
599             }
600 
601             ulCurrentCount = ( uint32_t ) CMT0.CMCNT;
602 
603             if( ulTickFlag != pdFALSE )
604             {
605                 /* The tick interrupt has already executed, although because
606                 this function is called with the scheduler suspended the actual
607                 tick processing will not occur until after this function has
608                 exited.  Reset the match value with whatever remains of this
609                 tick period. */
610                 ulMatchValue = ulMatchValueForOneTick - ulCurrentCount;
611                 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
612 
613                 /* The tick interrupt handler will already have pended the tick
614                 processing in the kernel.  As the pending tick will be
615                 processed as soon as this function exits, the tick value
616                 maintained by the tick is stepped forward by one less than the
617                 time spent sleeping.  The actual stepping of the tick appears
618                 later in this function. */
619                 ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
620             }
621             else
622             {
623                 /* Something other than the tick interrupt ended the sleep.
624                 How many complete tick periods passed while the processor was
625                 sleeping? */
626                 ulCompleteTickPeriods = ulCurrentCount / ulMatchValueForOneTick;
627 
628                 /* The match value is set to whatever fraction of a single tick
629                 period remains. */
630                 ulMatchValue = ulCurrentCount - ( ulCompleteTickPeriods * ulMatchValueForOneTick );
631                 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
632             }
633 
634             /* Restart the CMT so it runs up to the match value.  The match value
635             will get set to the value required to generate exactly one tick period
636             the next time the CMT interrupt executes. */
637             CMT0.CMCNT = 0;
638             CMT.CMSTR0.BIT.STR0 = 1;
639 
640             /* Wind the tick forward by the number of tick periods that the CPU
641             remained in a low power state. */
642             vTaskStepTick( ulCompleteTickPeriods );
643         }
644     }
645 
646 #endif /* configUSE_TICKLESS_IDLE */
647