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