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