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