2 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * SPDX-License-Identifier: MIT
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:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
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.
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
29 /*-----------------------------------------------------------
30 * Implementation of functions defined in portable.h for the SH2A port.
31 *----------------------------------------------------------*/
33 /* Standard C includes. */
36 /* Scheduler includes. */
40 /* Library includes. */
43 /* Hardware specifics. */
46 /*-----------------------------------------------------------*/
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 )
52 /* The peripheral clock is divided by this value before being supplying the
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
64 #define portCLOCK_DIVISOR 8UL
68 /* Keys required to lock and unlock access to certain system registers
70 #define portUNLOCK_KEY 0xA50B
71 #define portLOCK_KEY 0xA500
73 /*-----------------------------------------------------------*/
76 * Function to start the first task executing - written in asm code as direct
77 * access to registers is required.
79 extern void prvStartFirstTask( void );
82 * The tick ISR handler. The peripheral used is configured by the application
83 * via a hook/callback function.
85 __interrupt static void prvTickISR( void );
88 * Sets up the periodic ISR used for the RTOS tick using the CMT.
89 * The application writer can define configSETUP_TICK_INTERRUPT() (in
90 * FreeRTOSConfig.h) such that their own tick interrupt configuration is used
91 * in place of prvSetupTimerInterrupt().
93 static void prvSetupTimerInterrupt( void );
94 #ifndef configSETUP_TICK_INTERRUPT
96 /* The user has not provided their own tick interrupt configuration so use
97 * the definition in this file (which uses the interval timer). */
98 #define configSETUP_TICK_INTERRUPT() prvSetupTimerInterrupt()
99 #endif /* configSETUP_TICK_INTERRUPT */
102 * Called after the sleep mode registers have been configured, prvSleep()
103 * executes the pre and post sleep macros, and actually calls the wait
106 #if configUSE_TICKLESS_IDLE == 1
107 static void prvSleep( TickType_t xExpectedIdleTime );
108 #endif /* configUSE_TICKLESS_IDLE */
110 /*-----------------------------------------------------------*/
112 extern void * pxCurrentTCB;
114 /*-----------------------------------------------------------*/
116 /* Calculate how many clock increments make up a single tick period. */
117 static const uint32_t ulMatchValueForOneTick = ( ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) / configTICK_RATE_HZ );
119 #if configUSE_TICKLESS_IDLE == 1
121 /* Holds the maximum number of ticks that can be suppressed - which is
122 * basically how far into the future an interrupt can be generated. Set
123 * during initialisation. This is the maximum possible value that the
124 * compare match register can hold divided by ulMatchValueForOneTick. */
125 static const TickType_t xMaximumPossibleSuppressedTicks = USHRT_MAX / ( ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) / configTICK_RATE_HZ );
127 /* Flag set from the tick interrupt to allow the sleep processing to know if
128 * sleep mode was exited because of a tick interrupt, or an interrupt
129 * generated by something else. */
130 static volatile uint32_t ulTickFlag = pdFALSE;
132 /* The CMT counter is stopped temporarily each time it is re-programmed.
133 * The following constant offsets the CMT counter match value by the number of
134 * CMT counts that would typically be missed while the counter was stopped to
135 * compensate for the lost time. The large difference between the divided CMT
136 * clock and the CPU clock means it is likely ulStoppedTimerCompensation will
137 * equal zero - and be optimised away. */
138 static const uint32_t ulStoppedTimerCompensation = 100UL / ( configCPU_CLOCK_HZ / ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) );
140 #endif /* if configUSE_TICKLESS_IDLE == 1 */
142 /*-----------------------------------------------------------*/
145 * See header file for description.
147 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
148 TaskFunction_t pxCode,
149 void * pvParameters )
151 /* Offset to end up on 8 byte boundary. */
154 /* R0 is not included as it is the stack pointer. */
155 *pxTopOfStack = 0x00;
157 *pxTopOfStack = 0x00;
159 *pxTopOfStack = portINITIAL_PSW;
161 *pxTopOfStack = ( StackType_t ) pxCode;
163 /* When debugging it can be useful if every register is set to a known
164 * value. Otherwise code space can be saved by just setting the registers
165 * that need to be set. */
166 #ifdef USE_FULL_REGISTER_INITIALISATION
169 *pxTopOfStack = 0x12345678; /* r15. */
171 *pxTopOfStack = 0xaaaabbbb;
173 *pxTopOfStack = 0xdddddddd;
175 *pxTopOfStack = 0xcccccccc;
177 *pxTopOfStack = 0xbbbbbbbb;
179 *pxTopOfStack = 0xaaaaaaaa;
181 *pxTopOfStack = 0x99999999;
183 *pxTopOfStack = 0x88888888;
185 *pxTopOfStack = 0x77777777;
187 *pxTopOfStack = 0x66666666;
189 *pxTopOfStack = 0x55555555;
191 *pxTopOfStack = 0x44444444;
193 *pxTopOfStack = 0x33333333;
195 *pxTopOfStack = 0x22222222;
198 #else /* ifdef USE_FULL_REGISTER_INITIALISATION */
200 /* Leave space for the registers that will get popped from the stack
201 * when the task first starts executing. */
204 #endif /* ifdef USE_FULL_REGISTER_INITIALISATION */
206 *pxTopOfStack = ( StackType_t ) pvParameters; /* R1 */
208 *pxTopOfStack = 0x12345678; /* Accumulator. */
210 *pxTopOfStack = 0x87654321; /* Accumulator. */
214 /*-----------------------------------------------------------*/
216 BaseType_t xPortStartScheduler( void )
218 /* Use pxCurrentTCB just so it does not get optimised away. */
219 if( pxCurrentTCB != NULL )
221 /* Call an application function to set up the timer that will generate
222 * the tick interrupt. This way the application can decide which
223 * peripheral to use. If tickless mode is used then the default
224 * implementation defined in this file (which uses CMT0) should not be
226 configSETUP_TICK_INTERRUPT();
228 /* Enable the software interrupt. */
229 _IEN( _ICU_SWINT ) = 1;
231 /* Ensure the software interrupt is clear. */
232 _IR( _ICU_SWINT ) = 0;
234 /* Ensure the software interrupt is set to the kernel priority. */
235 _IPR( _ICU_SWINT ) = configKERNEL_INTERRUPT_PRIORITY;
237 /* Start the first task. */
241 /* Execution should not reach here as the tasks are now running!
242 * prvSetupTimerInterrupt() is called here to prevent the compiler outputting
243 * a warning about a statically declared function not being referenced in the
244 * case that the application writer has provided their own tick interrupt
245 * configuration routine (and defined configSETUP_TICK_INTERRUPT() such that
246 * their own routine will be called in place of prvSetupTimerInterrupt()). */
247 prvSetupTimerInterrupt();
249 /* Should not get here. */
252 /*-----------------------------------------------------------*/
254 #pragma vector = configTICK_VECTOR
255 __interrupt static void prvTickISR( void )
257 /* Re-enable interrupts. */
258 __enable_interrupt();
260 /* Increment the tick, and perform any processing the new tick value
262 __set_interrupt_level( configMAX_SYSCALL_INTERRUPT_PRIORITY );
264 if( xTaskIncrementTick() != pdFALSE )
269 __set_interrupt_level( configKERNEL_INTERRUPT_PRIORITY );
271 #if configUSE_TICKLESS_IDLE == 1
273 /* The CPU woke because of a tick. */
276 /* If this is the first tick since exiting tickless mode then the CMT
277 * compare match value needs resetting. */
278 CMT0.CMCOR = ( uint16_t ) ulMatchValueForOneTick;
282 /*-----------------------------------------------------------*/
284 void vPortEndScheduler( void )
286 /* Not implemented in ports where there is nothing to return to.
287 * Artificially force an assert. */
288 configASSERT( pxCurrentTCB == NULL );
290 /*-----------------------------------------------------------*/
292 static void prvSetupTimerInterrupt( void )
295 SYSTEM.PRCR.WORD = portUNLOCK_KEY;
301 SYSTEM.PRCR.WORD = portLOCK_KEY;
303 /* Interrupt on compare match. */
304 CMT0.CMCR.BIT.CMIE = 1;
306 /* Set the compare match value. */
307 CMT0.CMCOR = ( uint16_t ) ulMatchValueForOneTick;
309 /* Divide the PCLK. */
310 #if portCLOCK_DIVISOR == 512
312 CMT0.CMCR.BIT.CKS = 3;
314 #elif portCLOCK_DIVISOR == 128
316 CMT0.CMCR.BIT.CKS = 2;
318 #elif portCLOCK_DIVISOR == 32
320 CMT0.CMCR.BIT.CKS = 1;
322 #elif portCLOCK_DIVISOR == 8
324 CMT0.CMCR.BIT.CKS = 0;
326 #else /* if portCLOCK_DIVISOR == 512 */
328 #error Invalid portCLOCK_DIVISOR setting
330 #endif /* if portCLOCK_DIVISOR == 512 */
333 /* Enable the interrupt... */
334 _IEN( _CMT0_CMI0 ) = 1;
336 /* ...and set its priority to the application defined kernel priority. */
337 _IPR( _CMT0_CMI0 ) = configKERNEL_INTERRUPT_PRIORITY;
339 /* Start the timer. */
340 CMT.CMSTR0.BIT.STR0 = 1;
342 /*-----------------------------------------------------------*/
344 #if configUSE_TICKLESS_IDLE == 1
346 static void prvSleep( TickType_t xExpectedIdleTime )
348 /* Allow the application to define some pre-sleep processing. */
349 configPRE_SLEEP_PROCESSING( xExpectedIdleTime );
351 /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()
352 * means the application defined code has already executed the WAIT
354 if( xExpectedIdleTime > 0 )
356 __wait_for_interrupt();
359 /* Allow the application to define some post sleep processing. */
360 configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
363 #endif /* configUSE_TICKLESS_IDLE */
364 /*-----------------------------------------------------------*/
366 #if configUSE_TICKLESS_IDLE == 1
368 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
370 uint32_t ulMatchValue, ulCompleteTickPeriods, ulCurrentCount;
371 eSleepModeStatus eSleepAction;
373 /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */
375 /* Make sure the CMT reload value does not overflow the counter. */
376 if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
378 xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
381 /* Calculate the reload value required to wait xExpectedIdleTime tick
383 ulMatchValue = ulMatchValueForOneTick * xExpectedIdleTime;
385 if( ulMatchValue > ulStoppedTimerCompensation )
387 /* Compensate for the fact that the CMT is going to be stopped
389 ulMatchValue -= ulStoppedTimerCompensation;
392 /* Stop the CMT momentarily. The time the CMT is stopped for is
393 * accounted for as best it can be, but using the tickless mode will
394 * inevitably result in some tiny drift of the time maintained by the
395 * kernel with respect to calendar time. */
396 CMT.CMSTR0.BIT.STR0 = 0;
398 while( CMT.CMSTR0.BIT.STR0 == 1 )
400 /* Nothing to do here. */
403 /* Critical section using the global interrupt bit as the i bit is
404 * automatically reset by the WAIT instruction. */
405 __disable_interrupt();
407 /* The tick flag is set to false before sleeping. If it is true when
408 * sleep mode is exited then sleep mode was probably exited because the
409 * tick was suppressed for the entire xExpectedIdleTime period. */
410 ulTickFlag = pdFALSE;
412 /* If a context switch is pending then abandon the low power entry as
413 * the context switch might have been pended by an external interrupt that
414 * requires processing. */
415 eSleepAction = eTaskConfirmSleepModeStatus();
417 if( eSleepAction == eAbortSleep )
420 CMT.CMSTR0.BIT.STR0 = 1;
421 __enable_interrupt();
423 else if( eSleepAction == eNoTasksWaitingTimeout )
425 /* Protection off. */
426 SYSTEM.PRCR.WORD = portUNLOCK_KEY;
428 /* Ready for software standby with all clocks stopped. */
429 SYSTEM.SBYCR.BIT.SSBY = 1;
432 SYSTEM.PRCR.WORD = portLOCK_KEY;
434 /* Sleep until something happens. Calling prvSleep() will
435 * automatically reset the i bit in the PSW. */
436 prvSleep( xExpectedIdleTime );
438 /* Restart the CMT. */
439 CMT.CMSTR0.BIT.STR0 = 1;
443 /* Protection off. */
444 SYSTEM.PRCR.WORD = portUNLOCK_KEY;
446 /* Ready for deep sleep mode. */
447 SYSTEM.MSTPCRC.BIT.DSLPE = 1;
448 SYSTEM.MSTPCRA.BIT.MSTPA28 = 1;
449 SYSTEM.SBYCR.BIT.SSBY = 0;
452 SYSTEM.PRCR.WORD = portLOCK_KEY;
454 /* Adjust the match value to take into account that the current
455 * time slice is already partially complete. */
456 ulMatchValue -= ( uint32_t ) CMT0.CMCNT;
457 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
459 /* Restart the CMT to count up to the new match value. */
461 CMT.CMSTR0.BIT.STR0 = 1;
463 /* Sleep until something happens. Calling prvSleep() will
464 * automatically reset the i bit in the PSW. */
465 prvSleep( xExpectedIdleTime );
467 /* Stop CMT. Again, the time the SysTick is stopped for is
468 * accounted for as best it can be, but using the tickless mode will
469 * inevitably result in some tiny drift of the time maintained by the
470 * kernel with respect to calendar time. */
471 CMT.CMSTR0.BIT.STR0 = 0;
473 while( CMT.CMSTR0.BIT.STR0 == 1 )
475 /* Nothing to do here. */
478 ulCurrentCount = ( uint32_t ) CMT0.CMCNT;
480 if( ulTickFlag != pdFALSE )
482 /* The tick interrupt has already executed, although because
483 * this function is called with the scheduler suspended the actual
484 * tick processing will not occur until after this function has
485 * exited. Reset the match value with whatever remains of this
487 ulMatchValue = ulMatchValueForOneTick - ulCurrentCount;
488 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
490 /* The tick interrupt handler will already have pended the tick
491 * processing in the kernel. As the pending tick will be
492 * processed as soon as this function exits, the tick value
493 * maintained by the tick is stepped forward by one less than the
494 * time spent sleeping. The actual stepping of the tick appears
495 * later in this function. */
496 ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
500 /* Something other than the tick interrupt ended the sleep.
501 * How many complete tick periods passed while the processor was
503 ulCompleteTickPeriods = ulCurrentCount / ulMatchValueForOneTick;
505 /* The match value is set to whatever fraction of a single tick
507 ulMatchValue = ulCurrentCount - ( ulCompleteTickPeriods * ulMatchValueForOneTick );
508 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
511 /* Restart the CMT so it runs up to the match value. The match value
512 * will get set to the value required to generate exactly one tick period
513 * the next time the CMT interrupt executes. */
515 CMT.CMSTR0.BIT.STR0 = 1;
517 /* Wind the tick forward by the number of tick periods that the CPU
518 * remained in a low power state. */
519 vTaskStepTick( ulCompleteTickPeriods );
523 #endif /* configUSE_TICKLESS_IDLE */