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 #include "machine.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 /*
76 * Function to start the first task executing - written in asm code as direct
77 * access to registers is required.
78 */
79 extern void prvStartFirstTask( void );
80
81 /*
82 * The tick ISR handler. The peripheral used is configured by the application
83 * via a hook/callback function.
84 */
85 __interrupt static void prvTickISR( void );
86
87 /*
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().
92 */
93 static void prvSetupTimerInterrupt( void );
94 #ifndef configSETUP_TICK_INTERRUPT
95
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 */
100
101 /*
102 * Called after the sleep mode registers have been configured, prvSleep()
103 * executes the pre and post sleep macros, and actually calls the wait
104 * instruction.
105 */
106 #if configUSE_TICKLESS_IDLE == 1
107 static void prvSleep( TickType_t xExpectedIdleTime );
108 #endif /* configUSE_TICKLESS_IDLE */
109
110 /*-----------------------------------------------------------*/
111
112 extern void * pxCurrentTCB;
113
114 /*-----------------------------------------------------------*/
115
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 );
118
119 #if configUSE_TICKLESS_IDLE == 1
120
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 );
126
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;
131
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 ) );
139
140 #endif /* if configUSE_TICKLESS_IDLE == 1 */
141
142 /*-----------------------------------------------------------*/
143
144 /*
145 * See header file for description.
146 */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)147 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
148 TaskFunction_t pxCode,
149 void * pvParameters )
150 {
151 /* Offset to end up on 8 byte boundary. */
152 pxTopOfStack--;
153
154 /* R0 is not included as it is the stack pointer. */
155 *pxTopOfStack = 0x00;
156 pxTopOfStack--;
157 *pxTopOfStack = 0x00;
158 pxTopOfStack--;
159 *pxTopOfStack = portINITIAL_PSW;
160 pxTopOfStack--;
161 *pxTopOfStack = ( StackType_t ) pxCode;
162
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
167 {
168 pxTopOfStack--;
169 *pxTopOfStack = 0x12345678; /* r15. */
170 pxTopOfStack--;
171 *pxTopOfStack = 0xaaaabbbb;
172 pxTopOfStack--;
173 *pxTopOfStack = 0xdddddddd;
174 pxTopOfStack--;
175 *pxTopOfStack = 0xcccccccc;
176 pxTopOfStack--;
177 *pxTopOfStack = 0xbbbbbbbb;
178 pxTopOfStack--;
179 *pxTopOfStack = 0xaaaaaaaa;
180 pxTopOfStack--;
181 *pxTopOfStack = 0x99999999;
182 pxTopOfStack--;
183 *pxTopOfStack = 0x88888888;
184 pxTopOfStack--;
185 *pxTopOfStack = 0x77777777;
186 pxTopOfStack--;
187 *pxTopOfStack = 0x66666666;
188 pxTopOfStack--;
189 *pxTopOfStack = 0x55555555;
190 pxTopOfStack--;
191 *pxTopOfStack = 0x44444444;
192 pxTopOfStack--;
193 *pxTopOfStack = 0x33333333;
194 pxTopOfStack--;
195 *pxTopOfStack = 0x22222222;
196 pxTopOfStack--;
197 }
198 #else /* ifdef USE_FULL_REGISTER_INITIALISATION */
199 {
200 /* Leave space for the registers that will get popped from the stack
201 * when the task first starts executing. */
202 pxTopOfStack -= 15;
203 }
204 #endif /* ifdef USE_FULL_REGISTER_INITIALISATION */
205
206 *pxTopOfStack = ( StackType_t ) pvParameters; /* R1 */
207 pxTopOfStack--;
208 *pxTopOfStack = 0x12345678; /* Accumulator. */
209 pxTopOfStack--;
210 *pxTopOfStack = 0x87654321; /* Accumulator. */
211
212 return pxTopOfStack;
213 }
214 /*-----------------------------------------------------------*/
215
xPortStartScheduler(void)216 BaseType_t xPortStartScheduler( void )
217 {
218 /* Use pxCurrentTCB just so it does not get optimised away. */
219 if( pxCurrentTCB != NULL )
220 {
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
225 * overridden. */
226 configSETUP_TICK_INTERRUPT();
227
228 /* Enable the software interrupt. */
229 _IEN( _ICU_SWINT ) = 1;
230
231 /* Ensure the software interrupt is clear. */
232 _IR( _ICU_SWINT ) = 0;
233
234 /* Ensure the software interrupt is set to the kernel priority. */
235 _IPR( _ICU_SWINT ) = configKERNEL_INTERRUPT_PRIORITY;
236
237 /* Start the first task. */
238 prvStartFirstTask();
239 }
240
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();
248
249 /* Should not get here. */
250 return pdFAIL;
251 }
252 /*-----------------------------------------------------------*/
253
254 #pragma vector = configTICK_VECTOR
prvTickISR(void)255 __interrupt static void prvTickISR( void )
256 {
257 /* Re-enable interrupts. */
258 __enable_interrupt();
259
260 /* Increment the tick, and perform any processing the new tick value
261 * necessitates. */
262 __set_interrupt_level( configMAX_SYSCALL_INTERRUPT_PRIORITY );
263 {
264 if( xTaskIncrementTick() != pdFALSE )
265 {
266 taskYIELD();
267 }
268 }
269 __set_interrupt_level( configKERNEL_INTERRUPT_PRIORITY );
270
271 #if configUSE_TICKLESS_IDLE == 1
272 {
273 /* The CPU woke because of a tick. */
274 ulTickFlag = pdTRUE;
275
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;
279 }
280 #endif
281 }
282 /*-----------------------------------------------------------*/
283
vPortEndScheduler(void)284 void vPortEndScheduler( void )
285 {
286 /* Not implemented in ports where there is nothing to return to.
287 * Artificially force an assert. */
288 configASSERT( pxCurrentTCB == NULL );
289 }
290 /*-----------------------------------------------------------*/
291
prvSetupTimerInterrupt(void)292 static void prvSetupTimerInterrupt( void )
293 {
294 /* Unlock. */
295 SYSTEM.PRCR.WORD = portUNLOCK_KEY;
296
297 /* Enable CMT0. */
298 MSTP( CMT0 ) = 0;
299
300 /* Lock again. */
301 SYSTEM.PRCR.WORD = portLOCK_KEY;
302
303 /* Interrupt on compare match. */
304 CMT0.CMCR.BIT.CMIE = 1;
305
306 /* Set the compare match value. */
307 CMT0.CMCOR = ( uint16_t ) ulMatchValueForOneTick;
308
309 /* Divide the PCLK. */
310 #if portCLOCK_DIVISOR == 512
311 {
312 CMT0.CMCR.BIT.CKS = 3;
313 }
314 #elif portCLOCK_DIVISOR == 128
315 {
316 CMT0.CMCR.BIT.CKS = 2;
317 }
318 #elif portCLOCK_DIVISOR == 32
319 {
320 CMT0.CMCR.BIT.CKS = 1;
321 }
322 #elif portCLOCK_DIVISOR == 8
323 {
324 CMT0.CMCR.BIT.CKS = 0;
325 }
326 #else /* if portCLOCK_DIVISOR == 512 */
327 {
328 #error Invalid portCLOCK_DIVISOR setting
329 }
330 #endif /* if portCLOCK_DIVISOR == 512 */
331
332
333 /* Enable the interrupt... */
334 _IEN( _CMT0_CMI0 ) = 1;
335
336 /* ...and set its priority to the application defined kernel priority. */
337 _IPR( _CMT0_CMI0 ) = configKERNEL_INTERRUPT_PRIORITY;
338
339 /* Start the timer. */
340 CMT.CMSTR0.BIT.STR0 = 1;
341 }
342 /*-----------------------------------------------------------*/
343
344 #if configUSE_TICKLESS_IDLE == 1
345
prvSleep(TickType_t xExpectedIdleTime)346 static void prvSleep( TickType_t xExpectedIdleTime )
347 {
348 /* Allow the application to define some pre-sleep processing. */
349 configPRE_SLEEP_PROCESSING( xExpectedIdleTime );
350
351 /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()
352 * means the application defined code has already executed the WAIT
353 * instruction. */
354 if( xExpectedIdleTime > 0 )
355 {
356 __wait_for_interrupt();
357 }
358
359 /* Allow the application to define some post sleep processing. */
360 configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
361 }
362
363 #endif /* configUSE_TICKLESS_IDLE */
364 /*-----------------------------------------------------------*/
365
366 #if configUSE_TICKLESS_IDLE == 1
367
vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)368 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
369 {
370 uint32_t ulMatchValue, ulCompleteTickPeriods, ulCurrentCount;
371 eSleepModeStatus eSleepAction;
372
373 /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */
374
375 /* Make sure the CMT reload value does not overflow the counter. */
376 if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
377 {
378 xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
379 }
380
381 /* Calculate the reload value required to wait xExpectedIdleTime tick
382 * periods. */
383 ulMatchValue = ulMatchValueForOneTick * xExpectedIdleTime;
384
385 if( ulMatchValue > ulStoppedTimerCompensation )
386 {
387 /* Compensate for the fact that the CMT is going to be stopped
388 * momentarily. */
389 ulMatchValue -= ulStoppedTimerCompensation;
390 }
391
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;
397
398 while( CMT.CMSTR0.BIT.STR0 == 1 )
399 {
400 /* Nothing to do here. */
401 }
402
403 /* Critical section using the global interrupt bit as the i bit is
404 * automatically reset by the WAIT instruction. */
405 __disable_interrupt();
406
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;
411
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();
416
417 if( eSleepAction == eAbortSleep )
418 {
419 /* Restart tick. */
420 CMT.CMSTR0.BIT.STR0 = 1;
421 __enable_interrupt();
422 }
423 else if( eSleepAction == eNoTasksWaitingTimeout )
424 {
425 /* Protection off. */
426 SYSTEM.PRCR.WORD = portUNLOCK_KEY;
427
428 /* Ready for software standby with all clocks stopped. */
429 SYSTEM.SBYCR.BIT.SSBY = 1;
430
431 /* Protection on. */
432 SYSTEM.PRCR.WORD = portLOCK_KEY;
433
434 /* Sleep until something happens. Calling prvSleep() will
435 * automatically reset the i bit in the PSW. */
436 prvSleep( xExpectedIdleTime );
437
438 /* Restart the CMT. */
439 CMT.CMSTR0.BIT.STR0 = 1;
440 }
441 else
442 {
443 /* Protection off. */
444 SYSTEM.PRCR.WORD = portUNLOCK_KEY;
445
446 /* Ready for deep sleep mode. */
447 SYSTEM.MSTPCRC.BIT.DSLPE = 1;
448 SYSTEM.MSTPCRA.BIT.MSTPA28 = 1;
449 SYSTEM.SBYCR.BIT.SSBY = 0;
450
451 /* Protection on. */
452 SYSTEM.PRCR.WORD = portLOCK_KEY;
453
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;
458
459 /* Restart the CMT to count up to the new match value. */
460 CMT0.CMCNT = 0;
461 CMT.CMSTR0.BIT.STR0 = 1;
462
463 /* Sleep until something happens. Calling prvSleep() will
464 * automatically reset the i bit in the PSW. */
465 prvSleep( xExpectedIdleTime );
466
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;
472
473 while( CMT.CMSTR0.BIT.STR0 == 1 )
474 {
475 /* Nothing to do here. */
476 }
477
478 ulCurrentCount = ( uint32_t ) CMT0.CMCNT;
479
480 if( ulTickFlag != pdFALSE )
481 {
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
486 * tick period. */
487 ulMatchValue = ulMatchValueForOneTick - ulCurrentCount;
488 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
489
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;
497 }
498 else
499 {
500 /* Something other than the tick interrupt ended the sleep.
501 * How many complete tick periods passed while the processor was
502 * sleeping? */
503 ulCompleteTickPeriods = ulCurrentCount / ulMatchValueForOneTick;
504
505 /* The match value is set to whatever fraction of a single tick
506 * period remains. */
507 ulMatchValue = ulCurrentCount - ( ulCompleteTickPeriods * ulMatchValueForOneTick );
508 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
509 }
510
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. */
514 CMT0.CMCNT = 0;
515 CMT.CMSTR0.BIT.STR0 = 1;
516
517 /* Wind the tick forward by the number of tick periods that the CPU
518 * remained in a low power state. */
519 vTaskStepTick( ulCompleteTickPeriods );
520 }
521 }
522
523 #endif /* configUSE_TICKLESS_IDLE */
524