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