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 #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     /* The user has not provided their own tick interrupt configuration so use
96     the definition in this file (which uses the interval timer). */
97     #define configSETUP_TICK_INTERRUPT() prvSetupTimerInterrupt()
98 #endif /* configSETUP_TICK_INTERRUPT */
99 
100 /*
101  * Called after the sleep mode registers have been configured, prvSleep()
102  * executes the pre and post sleep macros, and actually calls the wait
103  * instruction.
104  */
105 #if configUSE_TICKLESS_IDLE == 1
106     static void prvSleep( TickType_t xExpectedIdleTime );
107 #endif /* configUSE_TICKLESS_IDLE */
108 
109 /*-----------------------------------------------------------*/
110 
111 extern void *pxCurrentTCB;
112 
113 /*-----------------------------------------------------------*/
114 
115 /* Calculate how many clock increments make up a single tick period. */
116 static const uint32_t ulMatchValueForOneTick = ( ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) / configTICK_RATE_HZ );
117 
118 #if configUSE_TICKLESS_IDLE == 1
119 
120     /* Holds the maximum number of ticks that can be suppressed - which is
121     basically how far into the future an interrupt can be generated. Set
122     during initialisation.  This is the maximum possible value that the
123     compare match register can hold divided by ulMatchValueForOneTick. */
124     static const TickType_t xMaximumPossibleSuppressedTicks = USHRT_MAX / ( ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) / configTICK_RATE_HZ );
125 
126     /* Flag set from the tick interrupt to allow the sleep processing to know if
127     sleep mode was exited because of a tick interrupt, or an interrupt
128     generated by something else. */
129     static volatile uint32_t ulTickFlag = pdFALSE;
130 
131     /* The CMT counter is stopped temporarily each time it is re-programmed.
132     The following constant offsets the CMT counter match value by the number of
133     CMT counts that would typically be missed while the counter was stopped to
134     compensate for the lost time.  The large difference between the divided CMT
135     clock and the CPU clock means it is likely ulStoppedTimerCompensation will
136     equal zero - and be optimised away. */
137     static const uint32_t ulStoppedTimerCompensation = 100UL / ( configCPU_CLOCK_HZ / ( configPERIPHERAL_CLOCK_HZ / portCLOCK_DIVISOR ) );
138 
139 #endif
140 
141 /*-----------------------------------------------------------*/
142 
143 /*
144  * See header file for description.
145  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)146 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
147 {
148     /* Offset to end up on 8 byte boundary. */
149     pxTopOfStack--;
150 
151     /* R0 is not included as it is the stack pointer. */
152     *pxTopOfStack = 0x00;
153     pxTopOfStack--;
154     *pxTopOfStack = 0x00;
155     pxTopOfStack--;
156     *pxTopOfStack = portINITIAL_PSW;
157     pxTopOfStack--;
158     *pxTopOfStack = ( StackType_t ) pxCode;
159 
160     /* When debugging it can be useful if every register is set to a known
161     value.  Otherwise code space can be saved by just setting the registers
162     that need to be set. */
163     #ifdef USE_FULL_REGISTER_INITIALISATION
164     {
165         pxTopOfStack--;
166         *pxTopOfStack = 0x12345678; /* r15. */
167         pxTopOfStack--;
168         *pxTopOfStack = 0xaaaabbbb;
169         pxTopOfStack--;
170         *pxTopOfStack = 0xdddddddd;
171         pxTopOfStack--;
172         *pxTopOfStack = 0xcccccccc;
173         pxTopOfStack--;
174         *pxTopOfStack = 0xbbbbbbbb;
175         pxTopOfStack--;
176         *pxTopOfStack = 0xaaaaaaaa;
177         pxTopOfStack--;
178         *pxTopOfStack = 0x99999999;
179         pxTopOfStack--;
180         *pxTopOfStack = 0x88888888;
181         pxTopOfStack--;
182         *pxTopOfStack = 0x77777777;
183         pxTopOfStack--;
184         *pxTopOfStack = 0x66666666;
185         pxTopOfStack--;
186         *pxTopOfStack = 0x55555555;
187         pxTopOfStack--;
188         *pxTopOfStack = 0x44444444;
189         pxTopOfStack--;
190         *pxTopOfStack = 0x33333333;
191         pxTopOfStack--;
192         *pxTopOfStack = 0x22222222;
193         pxTopOfStack--;
194     }
195     #else
196     {
197         /* Leave space for the registers that will get popped from the stack
198         when the task first starts executing. */
199         pxTopOfStack -= 15;
200     }
201     #endif
202 
203     *pxTopOfStack = ( StackType_t ) pvParameters; /* R1 */
204     pxTopOfStack--;
205     *pxTopOfStack = 0x12345678; /* Accumulator. */
206     pxTopOfStack--;
207     *pxTopOfStack = 0x87654321; /* Accumulator. */
208 
209     return pxTopOfStack;
210 }
211 /*-----------------------------------------------------------*/
212 
xPortStartScheduler(void)213 BaseType_t xPortStartScheduler( void )
214 {
215     /* Use pxCurrentTCB just so it does not get optimised away. */
216     if( pxCurrentTCB != NULL )
217     {
218         /* Call an application function to set up the timer that will generate
219         the tick interrupt.  This way the application can decide which
220         peripheral to use.  If tickless mode is used then the default
221         implementation defined in this file (which uses CMT0) should not be
222         overridden. */
223         configSETUP_TICK_INTERRUPT();
224 
225         /* Enable the software interrupt. */
226         _IEN( _ICU_SWINT ) = 1;
227 
228         /* Ensure the software interrupt is clear. */
229         _IR( _ICU_SWINT ) = 0;
230 
231         /* Ensure the software interrupt is set to the kernel priority. */
232         _IPR( _ICU_SWINT ) = configKERNEL_INTERRUPT_PRIORITY;
233 
234         /* Start the first task. */
235         prvStartFirstTask();
236     }
237 
238     /* Execution should not reach here as the tasks are now running!
239     prvSetupTimerInterrupt() is called here to prevent the compiler outputting
240     a warning about a statically declared function not being referenced in the
241     case that the application writer has provided their own tick interrupt
242     configuration routine (and defined configSETUP_TICK_INTERRUPT() such that
243     their own routine will be called in place of prvSetupTimerInterrupt()). */
244     prvSetupTimerInterrupt();
245 
246     /* Should not get here. */
247     return pdFAIL;
248 }
249 /*-----------------------------------------------------------*/
250 
251 #pragma vector = configTICK_VECTOR
prvTickISR(void)252 __interrupt static void prvTickISR( void )
253 {
254     /* Re-enable interrupts. */
255     __enable_interrupt();
256 
257     /* Increment the tick, and perform any processing the new tick value
258     necessitates. */
259     __set_interrupt_level( configMAX_SYSCALL_INTERRUPT_PRIORITY );
260     {
261         if( xTaskIncrementTick() != pdFALSE )
262         {
263             taskYIELD();
264         }
265     }
266     __set_interrupt_level( configKERNEL_INTERRUPT_PRIORITY );
267 
268     #if configUSE_TICKLESS_IDLE == 1
269     {
270         /* The CPU woke because of a tick. */
271         ulTickFlag = pdTRUE;
272 
273         /* If this is the first tick since exiting tickless mode then the CMT
274         compare match value needs resetting. */
275         CMT0.CMCOR = ( uint16_t ) ulMatchValueForOneTick;
276     }
277     #endif
278 }
279 /*-----------------------------------------------------------*/
280 
vPortEndScheduler(void)281 void vPortEndScheduler( void )
282 {
283     /* Not implemented in ports where there is nothing to return to.
284     Artificially force an assert. */
285     configASSERT( pxCurrentTCB == NULL );
286 }
287 /*-----------------------------------------------------------*/
288 
prvSetupTimerInterrupt(void)289 static void prvSetupTimerInterrupt( void )
290 {
291     /* Unlock. */
292     SYSTEM.PRCR.WORD = portUNLOCK_KEY;
293 
294     /* Enable CMT0. */
295     MSTP( CMT0 ) = 0;
296 
297     /* Lock again. */
298     SYSTEM.PRCR.WORD = portLOCK_KEY;
299 
300     /* Interrupt on compare match. */
301     CMT0.CMCR.BIT.CMIE = 1;
302 
303     /* Set the compare match value. */
304     CMT0.CMCOR = ( uint16_t ) ulMatchValueForOneTick;
305 
306     /* Divide the PCLK. */
307     #if portCLOCK_DIVISOR == 512
308     {
309         CMT0.CMCR.BIT.CKS = 3;
310     }
311     #elif portCLOCK_DIVISOR == 128
312     {
313         CMT0.CMCR.BIT.CKS = 2;
314     }
315     #elif portCLOCK_DIVISOR == 32
316     {
317         CMT0.CMCR.BIT.CKS = 1;
318     }
319     #elif portCLOCK_DIVISOR == 8
320     {
321         CMT0.CMCR.BIT.CKS = 0;
322     }
323     #else
324     {
325         #error Invalid portCLOCK_DIVISOR setting
326     }
327     #endif
328 
329 
330     /* Enable the interrupt... */
331     _IEN( _CMT0_CMI0 ) = 1;
332 
333     /* ...and set its priority to the application defined kernel priority. */
334     _IPR( _CMT0_CMI0 ) = configKERNEL_INTERRUPT_PRIORITY;
335 
336     /* Start the timer. */
337     CMT.CMSTR0.BIT.STR0 = 1;
338 }
339 /*-----------------------------------------------------------*/
340 
341 #if configUSE_TICKLESS_IDLE == 1
342 
prvSleep(TickType_t xExpectedIdleTime)343     static void prvSleep( TickType_t xExpectedIdleTime )
344     {
345         /* Allow the application to define some pre-sleep processing. */
346         configPRE_SLEEP_PROCESSING( xExpectedIdleTime );
347 
348         /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()
349         means the application defined code has already executed the WAIT
350         instruction. */
351         if( xExpectedIdleTime > 0 )
352         {
353             __wait_for_interrupt();
354         }
355 
356         /* Allow the application to define some post sleep processing. */
357         configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
358     }
359 
360 #endif /* configUSE_TICKLESS_IDLE */
361 /*-----------------------------------------------------------*/
362 
363 #if configUSE_TICKLESS_IDLE == 1
364 
vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)365     void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
366     {
367     uint32_t ulMatchValue, ulCompleteTickPeriods, ulCurrentCount;
368     eSleepModeStatus eSleepAction;
369 
370         /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */
371 
372         /* Make sure the CMT reload value does not overflow the counter. */
373         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
374         {
375             xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
376         }
377 
378         /* Calculate the reload value required to wait xExpectedIdleTime tick
379         periods. */
380         ulMatchValue = ulMatchValueForOneTick * xExpectedIdleTime;
381         if( ulMatchValue > ulStoppedTimerCompensation )
382         {
383             /* Compensate for the fact that the CMT is going to be stopped
384             momentarily. */
385             ulMatchValue -= ulStoppedTimerCompensation;
386         }
387 
388         /* Stop the CMT momentarily.  The time the CMT is stopped for is
389         accounted for as best it can be, but using the tickless mode will
390         inevitably result in some tiny drift of the time maintained by the
391         kernel with respect to calendar time. */
392         CMT.CMSTR0.BIT.STR0 = 0;
393         while( CMT.CMSTR0.BIT.STR0 == 1 )
394         {
395             /* Nothing to do here. */
396         }
397 
398         /* Critical section using the global interrupt bit as the i bit is
399         automatically reset by the WAIT instruction. */
400         __disable_interrupt();
401 
402         /* The tick flag is set to false before sleeping.  If it is true when
403         sleep mode is exited then sleep mode was probably exited because the
404         tick was suppressed for the entire xExpectedIdleTime period. */
405         ulTickFlag = pdFALSE;
406 
407         /* If a context switch is pending then abandon the low power entry as
408         the context switch might have been pended by an external interrupt that
409         requires processing. */
410         eSleepAction = eTaskConfirmSleepModeStatus();
411         if( eSleepAction == eAbortSleep )
412         {
413             /* Restart tick. */
414             CMT.CMSTR0.BIT.STR0 = 1;
415             __enable_interrupt();
416         }
417         else if( eSleepAction == eNoTasksWaitingTimeout )
418         {
419             /* Protection off. */
420             SYSTEM.PRCR.WORD = portUNLOCK_KEY;
421 
422             /* Ready for software standby with all clocks stopped. */
423             SYSTEM.SBYCR.BIT.SSBY = 1;
424 
425             /* Protection on. */
426             SYSTEM.PRCR.WORD = portLOCK_KEY;
427 
428             /* Sleep until something happens.  Calling prvSleep() will
429             automatically reset the i bit in the PSW. */
430             prvSleep( xExpectedIdleTime );
431 
432             /* Restart the CMT. */
433             CMT.CMSTR0.BIT.STR0 = 1;
434         }
435         else
436         {
437             /* Protection off. */
438             SYSTEM.PRCR.WORD = portUNLOCK_KEY;
439 
440             /* Ready for deep sleep mode. */
441             SYSTEM.MSTPCRC.BIT.DSLPE = 1;
442             SYSTEM.MSTPCRA.BIT.MSTPA28 = 1;
443             SYSTEM.SBYCR.BIT.SSBY = 0;
444 
445             /* Protection on. */
446             SYSTEM.PRCR.WORD = portLOCK_KEY;
447 
448             /* Adjust the match value to take into account that the current
449             time slice is already partially complete. */
450             ulMatchValue -= ( uint32_t ) CMT0.CMCNT;
451             CMT0.CMCOR = ( uint16_t ) ulMatchValue;
452 
453             /* Restart the CMT to count up to the new match value. */
454             CMT0.CMCNT = 0;
455             CMT.CMSTR0.BIT.STR0 = 1;
456 
457             /* Sleep until something happens.  Calling prvSleep() will
458             automatically reset the i bit in the PSW. */
459             prvSleep( xExpectedIdleTime );
460 
461             /* Stop CMT.  Again, the time the SysTick is stopped for is
462             accounted for as best it can be, but using the tickless mode will
463             inevitably result in some tiny drift of the time maintained by the
464             kernel with respect to calendar time. */
465             CMT.CMSTR0.BIT.STR0 = 0;
466             while( CMT.CMSTR0.BIT.STR0 == 1 )
467             {
468                 /* Nothing to do here. */
469             }
470 
471             ulCurrentCount = ( uint32_t ) CMT0.CMCNT;
472 
473             if( ulTickFlag != pdFALSE )
474             {
475                 /* The tick interrupt has already executed, although because
476                 this function is called with the scheduler suspended the actual
477                 tick processing will not occur until after this function has
478                 exited.  Reset the match value with whatever remains of this
479                 tick period. */
480                 ulMatchValue = ulMatchValueForOneTick - ulCurrentCount;
481                 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
482 
483                 /* The tick interrupt handler will already have pended the tick
484                 processing in the kernel.  As the pending tick will be
485                 processed as soon as this function exits, the tick value
486                 maintained by the tick is stepped forward by one less than the
487                 time spent sleeping.  The actual stepping of the tick appears
488                 later in this function. */
489                 ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
490             }
491             else
492             {
493                 /* Something other than the tick interrupt ended the sleep.
494                 How many complete tick periods passed while the processor was
495                 sleeping? */
496                 ulCompleteTickPeriods = ulCurrentCount / ulMatchValueForOneTick;
497 
498                 /* The match value is set to whatever fraction of a single tick
499                 period remains. */
500                 ulMatchValue = ulCurrentCount - ( ulCompleteTickPeriods * ulMatchValueForOneTick );
501                 CMT0.CMCOR = ( uint16_t ) ulMatchValue;
502             }
503 
504             /* Restart the CMT so it runs up to the match value.  The match value
505             will get set to the value required to generate exactly one tick period
506             the next time the CMT interrupt executes. */
507             CMT0.CMCNT = 0;
508             CMT.CMSTR0.BIT.STR0 = 1;
509 
510             /* Wind the tick forward by the number of tick periods that the CPU
511             remained in a low power state. */
512             vTaskStepTick( ulCompleteTickPeriods );
513         }
514     }
515 
516 #endif /* configUSE_TICKLESS_IDLE */
517