1 /*
2  * FreeRTOS Kernel <DEVELOPMENT BRANCH>
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 /* Scheduler includes. */
30 #include "FreeRTOS.h"
31 #include "task.h"
32 
33 /* This port uses xTaskGetCurrentTaskHandle to get TCB stack, it is required to
34  * enable this API. */
35 #if ( ( INCLUDE_xTaskGetCurrentTaskHandle != 1 ) && ( configNUMBER_OF_CORES == 1 ) )
36     #error INCLUDE_xTaskGetCurrentTaskHandle must be set to 1 in single core.
37 #endif
38 
39 /***********************************************************
40 * Macro definitions
41 ***********************************************************/
42 
43 /* Hardware specific macros */
44 #define portPSW_REGISTER_ID          ( 5 )
45 #define portFPSR_REGISTER_ID         ( 6 )
46 
47 /* PSW.EBV and PSW.CUx bits are kept as current status */
48 #define portINITIAL_PSW_MASK         ( 0x000f8000 )
49 #define portCURRENT_PSW_VALUE        ( portSTSR( portPSW_REGISTER_ID ) )
50 #define portCURRENT_SR_ZERO_VALUE    ( ( StackType_t ) 0x00000000 )
51 #define portCURRENT_FPSR_VALUE       ( portSTSR( portFPSR_REGISTER_ID ) )
52 
53 /* Mask for FPU configuration bits (FN, PEM, RM, FS) */
54 #define portINITIAL_FPSR_MASK        ( 0x00ae0000 )
55 #define portPSW_ID_MASK              ( 0x00000020 )
56 
57 /* Define necessary hardware IO for OSTM timer. OSTM0 is used by default as
58  * it is common for almost device variants. If it conflicts with application,
59  * the application shall implement another timer.*/
60 #define portOSTM_EIC_ADDR            ( 0xFFFFB0A8 )
61 #define portOSTM0CMP_ADDR            ( 0xFFD70000 )
62 #define portOSTM0CTL_ADDR            ( 0xFFD70020 )
63 #define portOSTM0TS_ADDR             ( 0xFFD70014 )
64 
65 #if ( configNUMBER_OF_CORES > 1 )
66 
67 /* IPIR  base address, the peripheral is used for Inter-Processor communication
68  * Hardware supports 4 channels which is offset by 0x0, 0x4, 0x8, 0xC bytes from
69  * base address. By default, channel 0 is selected. */
70     #ifdef configIPIR_CHANNEL
71         #define portIPIR_BASE_ADDR    ( ( 0xFFFEEC80 ) + ( configIPIR_CHANNEL << 2 ) )
72     #else
73         #define portIPIR_BASE_ADDR    ( 0xFFFEEC80 )
74     #endif
75 
76 /*  Address used for exclusive control for variable shared between PEs
77  * (common resources), each CPU cores have independent access path to
78  * this address. By default, G0MEV0 register is selected*/
79     #ifdef configEXCLUSIVE_ADDRESS
80         #define portMEV_BASE_ADDR    configEXCLUSIVE_ADDRESS
81     #else
82         #define portMEV_BASE_ADDR    ( 0xFFFEEC00 )
83     #endif
84 #endif /* if ( configNUMBER_OF_CORES > 1 ) */
85 
86 /* Macros required to set up the initial stack. */
87 #define portSTACK_INITIAL_VALUE_R1     ( ( StackType_t ) 0x01010101 )
88 #define portSTACK_INITIAL_VALUE_R2     ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x02 )
89 #define portSTACK_INITIAL_VALUE_R3     ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x03 )
90 #define portSTACK_INITIAL_VALUE_R4     ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x04 )
91 #define portSTACK_INITIAL_VALUE_R5     ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x05 )
92 #define portSTACK_INITIAL_VALUE_R6     ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x06 )
93 #define portSTACK_INITIAL_VALUE_R7     ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x07 )
94 #define portSTACK_INITIAL_VALUE_R8     ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x08 )
95 #define portSTACK_INITIAL_VALUE_R9     ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x09 )
96 #define portSTACK_INITIAL_VALUE_R10    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x10 )
97 #define portSTACK_INITIAL_VALUE_R11    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x11 )
98 #define portSTACK_INITIAL_VALUE_R12    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x12 )
99 #define portSTACK_INITIAL_VALUE_R13    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x13 )
100 #define portSTACK_INITIAL_VALUE_R14    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x14 )
101 #define portSTACK_INITIAL_VALUE_R15    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x15 )
102 #define portSTACK_INITIAL_VALUE_R16    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x16 )
103 #define portSTACK_INITIAL_VALUE_R17    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x17 )
104 #define portSTACK_INITIAL_VALUE_R18    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x18 )
105 #define portSTACK_INITIAL_VALUE_R19    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x19 )
106 #define portSTACK_INITIAL_VALUE_R20    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x20 )
107 #define portSTACK_INITIAL_VALUE_R21    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x21 )
108 #define portSTACK_INITIAL_VALUE_R22    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x22 )
109 #define portSTACK_INITIAL_VALUE_R23    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x23 )
110 #define portSTACK_INITIAL_VALUE_R24    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x24 )
111 #define portSTACK_INITIAL_VALUE_R25    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x25 )
112 #define portSTACK_INITIAL_VALUE_R26    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x26 )
113 #define portSTACK_INITIAL_VALUE_R27    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x27 )
114 #define portSTACK_INITIAL_VALUE_R28    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x28 )
115 #define portSTACK_INITIAL_VALUE_R29    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x29 )
116 #define portSTACK_INITIAL_VALUE_R30    ( ( StackType_t ) portSTACK_INITIAL_VALUE_R1 * 0x30 )
117 
118 /***********************************************************
119 * Typedef definitions
120 ***********************************************************/
121 
122 /* OSTM Count Start Trigger Register (OSTMnTS) */
123 #define portOSTM_COUNTER_START              ( 0x01U ) /* Starts the counter */
124 
125 /* OSTM Count Stop Trigger Register (OSTMnTT) */
126 #define portOSTM_COUNTER_STOP               ( 0x01U ) /* Stops the counter */
127 
128 /* OSTM Control Register (OSTMnCTL) */
129 #define portOSTM_MODE_INTERVAL_TIMER        ( 0x00U )
130 #define portOSTM_MODE_FREE_RUNNING          ( 0x02U )
131 
132 /* Disables or Enable the interrupts when counting starts */
133 #define portOSTM_START_INTERRUPT_DISABLE    ( 0x00U )
134 #define portOSTM_START_INTERRUPT_ENABLE     ( 0x01U )
135 
136 /* Interrupt vector method select (TBxxx) */
137 #define portINT_DIRECT_VECTOR               ( 0x0U )
138 #define portINT_TABLE_VECTOR                ( 0x1U )
139 
140 /* Interrupt mask (MKxxx) */
141 #define portINT_PROCESSING_ENABLED          ( 0x0U )
142 #define portINT_PROCESSING_DISABLED         ( 0x1U )
143 
144 /* Specify 16 interrupt priority levels */
145 #define portINT_PRIORITY_HIGHEST            ( 0x0000U ) /* Level 0 (highest) */
146 #define portINT_PRIORITY_LEVEL1             ( 0x0001U ) /* Level 1 */
147 #define portINT_PRIORITY_LEVEL2             ( 0x0002U ) /* Level 2 */
148 #define portINT_PRIORITY_LEVEL3             ( 0x0003U ) /* Level 3 */
149 #define portINT_PRIORITY_LEVEL4             ( 0x0004U ) /* Level 4 */
150 #define portINT_PRIORITY_LEVEL5             ( 0x0005U ) /* Level 5 */
151 #define portINT_PRIORITY_LEVEL6             ( 0x0006U ) /* Level 6 */
152 #define portINT_PRIORITY_LEVEL7             ( 0x0007U ) /* Level 7 */
153 #define portINT_PRIORITY_LEVEL8             ( 0x0008U ) /* Level 8 */
154 #define portINT_PRIORITY_LEVEL9             ( 0x0009U ) /* Level 9 */
155 #define portINT_PRIORITY_LEVEL10            ( 0x000AU ) /* Level 10 */
156 #define portINT_PRIORITY_LEVEL11            ( 0x000BU ) /* Level 11 */
157 #define portINT_PRIORITY_LEVEL12            ( 0x000CU ) /* Level 12 */
158 #define portINT_PRIORITY_LEVEL13            ( 0x000DU ) /* Level 13 */
159 #define portINT_PRIORITY_LEVEL14            ( 0x000EU ) /* Level 14 */
160 #define portINT_PRIORITY_LOWEST             ( 0x000FU ) /* Level 15 (lowest) */
161 
162 /* Macros indicating status of scheduler request */
163 #define PORT_SCHEDULER_NOREQUEST            0UL
164 #define PORT_SCHEDULER_TASKSWITCH           1UL       /* Do not modify */
165 #define PORT_SCHEDULER_STARTFIRSTTASK       2UL       /* Do not modify */
166 
167 #ifndef configSETUP_TICK_INTERRUPT
168 
169 /* The user has not provided their own tick interrupt configuration so use
170  * the definition in this file (which uses the interval timer). */
171     #define configSETUP_TICK_INTERRUPT()    prvSetupTimerInterrupt()
172 #endif /* configSETUP_TICK_INTERRUPT */
173 
174 #if ( !defined( configMAX_INT_NESTING ) || ( configMAX_INT_NESTING == 0 ) )
175 
176 /* Set the default value for depth of nested interrupt. In theory, the
177  * microcontroller have mechanism to limit number of nested level of interrupt
178  * by priority (maximum 16 levels). However, the large stack memory should be
179  * prepared for each task to save resource in interrupt handler. Therefore, it
180  * is necessary to limit depth of nesting interrupt to optimize memory usage.
181  * In addition, the execution time of interrupt handler should be very short
182  * (typically not exceed 20us), this constraint does not impact to system.
183  */
184     #define configMAX_INT_NESTING    2UL
185 #endif
186 
187 /*
188  * Used to catch tasks that attempt to return from their implementing function.
189  */
190 static void prvTaskExitError( void );
191 
192 /*
193  * Sets up the periodic ISR used for the RTOS tick using the OSTM.
194  * The application writer can define configSETUP_TICK_INTERRUPT() (in
195  * FreeRTOSConfig.h) such that their own tick interrupt configuration is used
196  * in place of prvSetupTimerInterrupt().
197  */
198 static void prvSetupTimerInterrupt( void );
199 
200 #if ( configNUMBER_OF_CORES > 1 )
201 
202 /*
203  * Functions implement spin-lock between cores by atomic accesses to Exclusive
204  * Control Register (G0MEVm). There are separated access path between CPU cores,
205  * but they should wait if access to same register
206  */
207     static void prvExclusiveLock( BaseType_t xFromIsr );
208     static void prvExclusiveRelease( BaseType_t xFromIsr );
209 
210 #endif
211 
212 /*
213  * Function to start the first task executing
214  */
215 extern void vPortStartFirstTask( void );
216 
217 /* Scheduler request on each cores which are starting first task and switching
218  * context */
219 volatile BaseType_t xPortScheduleStatus[ configNUMBER_OF_CORES ] = { 0 };
220 
221 /* Counts the interrupt nesting depth. A context switch is only performed if
222  * the nesting depth is 0. In addition, the interrupt shares same stack
223  * allocated for each tasks. With supporting nesting interrupt, the stack
224  * may be overflowed.
225  * It is necessary to control maximum stack depth.
226  */
227 volatile UBaseType_t uxInterruptNesting[ configNUMBER_OF_CORES ] = { 0 };
228 volatile const UBaseType_t uxPortMaxInterruptDepth = configMAX_INT_NESTING;
229 
230 /* Count number of nested locks by same cores. The lock is completely released
231  * only if this count is decreased to 0, the lock is separated for task
232  * and isr */
233 UBaseType_t uxLockNesting[ configNUMBER_OF_CORES ][ 2 ] = { 0 };
234 
235 #if ( configNUMBER_OF_CORES > 1 )
236 
237 /* Pointer to exclusive access memory */
238     volatile BaseType_t * pxPortExclusiveReg = ( volatile BaseType_t * ) ( portMEV_BASE_ADDR );
239 #endif
240 
241 /* Interrupt handler for OSTM timer which handling tick increment and resulting
242  * to switch context. */
243 void vPortTickISR( void );
244 
245 #if ( configNUMBER_OF_CORES > 1 )
246 
247 /* Yield specific cores by send inter-processor interrupt */
248     void vPortYieldCore( uint32_t xCoreID );
249 
250 /*
251  * Inter-processor interrupt handler. The interrupt is triggered by
252  * portYIELD_CORE().
253  */
254     void vPortIPIHander( void );
255 
256 /* These functions below implement recursive spinlock for exclusive access among
257  * cores. The core will wait until lock will be available, whilst the core which
258  * already had lock can acquire lock without waiting. This function could be
259  * call from task and interrupt context, the critical section is called
260  * as in ISR */
261     void vPortRecursiveLockAcquire( BaseType_t xCoreID, BaseType_t xFromIsr );
262     void vPortRecursiveLockRelease( BaseType_t xCoreID, BaseType_t xFromIsr );
263 
264 #endif /* (configNUMBER_OF_CORES > 1) */
265 
266 /*-----------------------------------------------------------*/
267 
268 /*
269  * These below functions implement interrupt mask from interrupt. They are not
270  * called in nesting, it is protected by FreeRTOS kernel.
271  */
xPortSetInterruptMask(void)272 portLONG xPortSetInterruptMask( void )
273 {
274     portLONG ulPSWValue = portSTSR( portPSW_REGISTER_ID );
275 
276     portDISABLE_INTERRUPTS();
277 
278     /* It returns current value of Program Status Word register */
279     return ulPSWValue;
280 }
281 
282 /*-----------------------------------------------------------*/
283 
vPortClearInterruptMask(portLONG uxSavedInterruptStatus)284 void vPortClearInterruptMask( portLONG uxSavedInterruptStatus )
285 {
286     portLONG ulPSWValue = portSTSR( portPSW_REGISTER_ID );
287 
288     /* Interrupt Disable status is indicates by bit#5 of PSW
289     * (1: Interrupt is disabled; 0: Interrupt is enabled) */
290 
291     /* Revert to the status before interrupt mask. */
292     ulPSWValue &= ( ~( portPSW_ID_MASK ) );
293     ulPSWValue |= ( portPSW_ID_MASK & uxSavedInterruptStatus );
294     portLDSR( portPSW_REGISTER_ID, ulPSWValue );
295 }
296 
297 /*-----------------------------------------------------------*/
298 
299 /*
300  * Using CC-RH intrinsic function to get HTCFG0 (regID, selID) = (0,2)
301  * Core ID is indicates by bit HTCFG0.PEID located at bit 18 to 16
302  * Bit 31 to 19 are read only and always be read as 0. HTCFG0.PEID is 1 and 2
303  * corresponding to core 0 (PE1) and core 1 (PE2). It is adjusted to 0 and 1.
304  */
xPortGET_CORE_ID(void)305 BaseType_t xPortGET_CORE_ID( void )
306 {
307     #if ( configNUMBER_OF_CORES > 1 )
308         return ( portSTSR_CCRH( 0, 2 ) >> 16 ) - 1;
309     #else
310 
311         /* In single core, xPortGET_CORE_ID is used in this port only.
312          * The dummy core ID could be controlled inside this port. */
313         return 0;
314     #endif
315 }
316 
317 /*-----------------------------------------------------------*/
318 
319 /*
320  * This port supports both multi-cores and single-core, whilst TCB stack
321  * variables are different which are respectively pxCurrentTCB (single-core)
322  * and pxCurrentTCBs[] (multiple-cores). This function is defined to obtains
323  * TCBs of current cores. Also, the C function could switch to corresponding
324  * pointer by pre-compile conditions.
325  */
pvPortGetCurrentTCB(void)326 void * pvPortGetCurrentTCB( void )
327 {
328     void * pvCurrentTCB = ( void * ) xTaskGetCurrentTaskHandle();
329 
330     configASSERT( pvCurrentTCB != NULL );
331 
332     return pvCurrentTCB;
333 }
334 
335 /*-----------------------------------------------------------*/
336 
337 /*
338  * This function checks if a context switch is required and, if so, updates
339  * the scheduler status for the core on which the function is called. The
340  * scheduler status is set to indicate that a task switch should occur.
341  */
vPortSetSwitch(BaseType_t xSwitchRequired)342 void vPortSetSwitch( BaseType_t xSwitchRequired )
343 {
344     if( xSwitchRequired != pdFALSE )
345     {
346         xPortScheduleStatus[ xPortGET_CORE_ID() ] = PORT_SCHEDULER_TASKSWITCH;
347     }
348 }
349 
350 /*-----------------------------------------------------------*/
351 
352 /*
353  * Setup the stack of a new task so it is ready to be placed under the
354  * scheduler control. The registers have to be placed on the stack in the
355  * order that the port expects to find them.
356  *
357  * @param[in]  pxTopOfStack  Pointer to top of this task's stack
358  * @param[in]  pxCode        Task function, stored as initial PC for the task
359  * @param[in]  pvParameters  Parameters for task
360  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)361 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
362                                      TaskFunction_t pxCode,
363                                      void * pvParameters )
364 {
365     /* Simulate the stack frame as it would be created by
366      * a context switch interrupt. */
367     *pxTopOfStack = ( StackType_t ) prvTaskExitError;            /* R31 (LP) */
368     pxTopOfStack--;
369     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R5;  /* R5 (TP)  */
370     pxTopOfStack--;
371     *pxTopOfStack = ( StackType_t ) pvParameters;                /* R6       */
372     pxTopOfStack--;
373     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R7;  /* R7       */
374     pxTopOfStack--;
375     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R8;  /* R8       */
376     pxTopOfStack--;
377     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R9;  /* R9       */
378     pxTopOfStack--;
379     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R10; /* R10      */
380     pxTopOfStack--;
381     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R11; /* R11      */
382     pxTopOfStack--;
383     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R12; /* R12      */
384     pxTopOfStack--;
385     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R13; /* R13      */
386     pxTopOfStack--;
387     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R14; /* R14      */
388     pxTopOfStack--;
389     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R15; /* R15      */
390     pxTopOfStack--;
391     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R16; /* R16      */
392     pxTopOfStack--;
393     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R17; /* R17      */
394     pxTopOfStack--;
395     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R18; /* R18      */
396     pxTopOfStack--;
397     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R19; /* R19      */
398     pxTopOfStack--;
399     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R20; /* R20      */
400     pxTopOfStack--;
401     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R21; /* R21      */
402     pxTopOfStack--;
403     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R22; /* R22      */
404     pxTopOfStack--;
405     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R23; /* R23      */
406     pxTopOfStack--;
407     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R24; /* R24      */
408     pxTopOfStack--;
409     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R25; /* R25      */
410     pxTopOfStack--;
411     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R26; /* R26      */
412     pxTopOfStack--;
413     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R27; /* R27      */
414     pxTopOfStack--;
415     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R28; /* R28      */
416     pxTopOfStack--;
417     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R29; /* R29      */
418     pxTopOfStack--;
419     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R30; /* R30 (EP) */
420     pxTopOfStack--;
421     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R1;  /* R1        */
422     pxTopOfStack--;
423     *pxTopOfStack = ( StackType_t ) portSTACK_INITIAL_VALUE_R2;  /* R2        */
424 
425     pxTopOfStack--;
426 
427     /* Keep System pre-configuration (HV, CUx, EBV) as current setting in
428      * PSW register */
429     *pxTopOfStack = ( StackType_t ) ( portCURRENT_PSW_VALUE & portINITIAL_PSW_MASK ); /* EIPSW */
430     pxTopOfStack--;
431     *pxTopOfStack = ( StackType_t ) pxCode;                                           /* EIPC */
432     pxTopOfStack--;
433     *pxTopOfStack = ( StackType_t ) portCURRENT_SR_ZERO_VALUE;                        /* EIIC */
434     pxTopOfStack--;
435     *pxTopOfStack = ( StackType_t ) ( portCURRENT_PSW_VALUE & portINITIAL_PSW_MASK ); /* CTPSW */
436     pxTopOfStack--;
437     *pxTopOfStack = ( StackType_t ) portCURRENT_SR_ZERO_VALUE;                        /* CTPC */
438 
439 /* __FPU is defined by CCRH compiler if FPU is enabled */
440     #if ( configENABLE_FPU == 1 )
441         pxTopOfStack--;
442         *pxTopOfStack = ( StackType_t ) ( portCURRENT_FPSR_VALUE & portINITIAL_FPSR_MASK ); /* FPSR */
443         pxTopOfStack--;
444         *pxTopOfStack = ( StackType_t ) portCURRENT_SR_ZERO_VALUE;                          /* FPEPC */
445     #endif /* (configENABLE_FPU == 1) */
446 
447     return pxTopOfStack;
448 }
449 
450 /*-----------------------------------------------------------*/
451 
452 /*
453  * Configures the tick frequency and starts the first task.
454  */
xPortStartScheduler(void)455 BaseType_t xPortStartScheduler( void )
456 {
457     #if ( configNUMBER_OF_CORES > 1 )
458         BaseType_t xCurrentCore = xPortGET_CORE_ID();
459     #endif
460 
461     /* Prevent interrupt by timer interrupt during starting first task.
462      * The interrupt shall be enabled automatically by being restored from
463      * task stack */
464     portDISABLE_INTERRUPTS();
465 
466     /* Setup the tick interrupt */
467     configSETUP_TICK_INTERRUPT();
468 
469     #if ( configNUMBER_OF_CORES > 1 )
470         /* Start scheduler on other cores */
471         for( uint16_t xCoreID = 0; xCoreID < configNUMBER_OF_CORES; xCoreID++ )
472         {
473             if( xCoreID != xCurrentCore )
474             {
475                 /* Send yielding request to other cores with flag to start
476                  * first task. TaskContextSwitch is not executed */
477                 xPortScheduleStatus[ xCoreID ] = PORT_SCHEDULER_STARTFIRSTTASK;
478                 vPortYieldCore( xCoreID );
479             }
480             else
481             {
482                 /* Nothing to do. The first task is started in this call by
483                  * below vPortStartFirstTask() */
484                 xPortScheduleStatus[ xCoreID ] = PORT_SCHEDULER_NOREQUEST;
485             }
486         }
487     #endif /* if ( configNUMBER_OF_CORES > 1 ) */
488 
489     /* Start first task in primary core */
490     vPortStartFirstTask();
491 
492     /* Should never get here as the tasks will now be executing! */
493     prvTaskExitError();
494 
495     /* To prevent compiler warnings in the case that the application writer
496      * overrides this functionality by defining configTASK_RETURN_ADDRESS.
497      * Call vTaskSwitchContext() so link time optimization does not remove
498      * the symbol. */
499     vTaskSwitchContext(
500         #if ( configNUMBER_OF_CORES > 1 )
501             xCurrentCore
502         #endif
503         );
504 
505     return pdFALSE;
506 }
507 
508 /*-----------------------------------------------------------*/
509 
510 /*
511  * Used to catch tasks that attempt to return from their implementing function.
512  */
prvTaskExitError(void)513 static void prvTaskExitError( void )
514 {
515     /* A function that implements a task must not exit or attempt to return to
516      * its caller as there is nothing to return to.  If a task wants to exit it
517      * should instead call vTaskDelete( NULL ).
518      *
519      * Artificially force an assert() to be triggered if configASSERT() is
520      * defined, then stop here so application writers can catch the error. */
521 
522     /* This statement will always fail, triggering the assert */
523     configASSERT( pdFALSE );
524 
525     /*
526      * The following statement may be unreachable because configASSERT(pdFALSE)
527      * always triggers an assertion failure, which typically halts program
528      * execution.
529      * The warning may be reported to indicate to indicate that the compiler
530      * detects the subsequent code will not be executed.
531      * The warning is acceptable to ensure program is halt regardless of
532      * configASSERT(pdFALSE) implementation
533      */
534     portDISABLE_INTERRUPTS();
535 
536     for( ; ; )
537     {
538         /* Infinite loop to ensure the function does not return. */
539     }
540 }
541 
542 /*-----------------------------------------------------------*/
543 
vPortEndScheduler(void)544 void vPortEndScheduler( void )
545 {
546     /* Not implemented in ports where there is nothing to return to.
547      * Artificially force an assert. */
548     configASSERT( pdFALSE );
549 }
550 
551 /*-----------------------------------------------------------*/
552 
553 #if ( configNUMBER_OF_CORES > 1 )
554 
vPortYieldCore(uint32_t xCoreID)555     void vPortYieldCore( uint32_t xCoreID )
556     {
557         /* Check if we need to yield on a different core */
558         if( xCoreID != xPortGET_CORE_ID() )
559         {
560             volatile uint32_t * pulIPIRReg;
561 
562             /* Determine the IPI register based on the target core ID */
563             pulIPIRReg = ( volatile uint32_t * ) ( portIPIR_BASE_ADDR );
564 
565             /*Inter-processor interrupt generates an interrupt request by
566              * writing 1 to applicable bits of target cores. The interrupt
567              * should be enabled by application in  corresponding cores
568              * including PSW.ID (EI instruction) and interrupt control setting
569              * for ICIPIRn channel (interrupt mask, vector method)
570              */
571             *pulIPIRReg = ( 1 << xCoreID );
572         }
573         else
574         {
575             /* Yielding current core */
576             vPortYield();
577         }
578     }
579 
580 /*-----------------------------------------------------------*/
581 
582 /*
583  * Handler for inter-processor interrupt in second cores. The interrupt is
584  * triggered by portYIELD_CORE(). vTaskSwitchContext() is invoked to
585  * switch tasks
586  */
vPortIPIHander(void)587     void vPortIPIHander( void )
588     {
589         BaseType_t xCurrentCore = xPortGET_CORE_ID();
590 
591         /* 1st execution starts 1st task, TaskSwitchContext is not executed */
592         if( PORT_SCHEDULER_STARTFIRSTTASK != xPortScheduleStatus[ xCurrentCore ] )
593         {
594             xPortScheduleStatus[ xCurrentCore ] = PORT_SCHEDULER_TASKSWITCH;
595         }
596     }
597 
598 /*-----------------------------------------------------------*/
599 
600 #endif /* (configNUMBER_OF_CORES > 1) */
601 
vPortTickISR(void)602 void vPortTickISR( void )
603 {
604     /* In case of multicores with SMP,  xTaskIncrementTick is required to
605      * called in critical section to avoid conflict resource as this function
606      * could be called by xTaskResumeAll() from any cores. */
607     #if ( configNUMBER_OF_CORES > 1 )
608         BaseType_t xSavedInterruptStatus;
609 
610         xSavedInterruptStatus = portENTER_CRITICAL_FROM_ISR();
611     #endif
612     {
613         /* Increment the RTOS tick. */
614         if( xTaskIncrementTick() != pdFALSE )
615         {
616             /* Pend a context switch. */
617             xPortScheduleStatus[ xPortGET_CORE_ID() ] = PORT_SCHEDULER_TASKSWITCH;
618         }
619     }
620     #if ( configNUMBER_OF_CORES > 1 )
621         portEXIT_CRITICAL_FROM_ISR( xSavedInterruptStatus );
622     #endif
623 }
624 
625 /*-----------------------------------------------------------*/
626 
prvSetupTimerInterrupt(void)627 static void prvSetupTimerInterrupt( void )
628 {
629     volatile uint32_t * pulOSTMIntReg;
630 
631     /* Interrupt configuration for OSTM Timer
632      * By default, the second lowest priority is set for timer interrupt to
633      * avoid blocking other interrupt. Normally, user could set the lowest
634      * priority for non-critical event. It try to keep timer on time.
635      * In addition, direct vector table is used by default.
636      */
637     pulOSTMIntReg = ( volatile uint32_t * ) portOSTM_EIC_ADDR;
638     *pulOSTMIntReg = ( portINT_PROCESSING_ENABLED | portINT_DIRECT_VECTOR | portINT_PRIORITY_LEVEL14 );
639 
640     /* Set OSTM0 control setting */
641     *( ( volatile uint32_t * ) portOSTM0CTL_ADDR ) =
642         ( portOSTM_MODE_INTERVAL_TIMER | portOSTM_START_INTERRUPT_DISABLE );
643     *( ( volatile uint32_t * ) portOSTM0CMP_ADDR ) =
644         ( ( configCPU_CLOCK_HZ / configTIMER_PRESCALE ) / configTICK_RATE_HZ ) - 1;
645 
646     /* Enable OSTM0 operation */
647     *( ( volatile uint32_t * ) portOSTM0TS_ADDR ) = portOSTM_COUNTER_START;
648 }
649 
650 /*-----------------------------------------------------------*/
651 
652 #if ( configNUMBER_OF_CORES > 1 )
653 
654 /*
655  * These functions implement spin-lock mechanism among cores using hardware
656  * exclusive control with atomic access by CLR1 and SET1 instruction.
657  * Nesting calls to these APIs are possible.
658  */
659     #pragma inline_asm prvExclusiveLock
prvExclusiveLock(BaseType_t xBitPosition)660     static void prvExclusiveLock( BaseType_t xBitPosition )
661     {
662         /* No problem with r19, CCRH does not required to restore same value
663          * before and after function call. */
664         mov     # _pxPortExclusiveReg, r19
665         ld.w    0[ r19 ], r19
666 
667 prvExclusiveLock_Lock:
668 
669         /* r6 is xBitPosition */
670         set1 r6, [ r19 ]
671         bz prvExclusiveLock_Lock_success
672         snooze
673         br prvExclusiveLock_Lock
674 
675 prvExclusiveLock_Lock_success:
676     }
677 
678 /*-----------------------------------------------------------*/
679 
680     #pragma inline_asm prvExclusiveRelease
prvExclusiveRelease(BaseType_t xBitPosition)681     static void prvExclusiveRelease( BaseType_t xBitPosition )
682     {
683         mov     # _pxPortExclusiveReg, r19
684         ld.w    0[ r19 ], r19
685 
686         /* r6 is xBitPosition */
687         clr1 r6, [ r19 ]
688     }
689 
690 /*-----------------------------------------------------------*/
vPortRecursiveLockAcquire(BaseType_t xCoreID,BaseType_t xFromIsr)691     void vPortRecursiveLockAcquire( BaseType_t xCoreID, BaseType_t xFromIsr )
692     {
693         BaseType_t xSavedInterruptStatus;
694         BaseType_t xBitPosition = ( xFromIsr == pdTRUE );
695 
696         xSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
697 
698         if( uxLockNesting[ xCoreID ][ xBitPosition ] == 0 )
699         {
700             prvExclusiveLock( xBitPosition );
701         }
702 
703         uxLockNesting[ xCoreID ][ xBitPosition ]++;
704         portCLEAR_INTERRUPT_MASK_FROM_ISR( xSavedInterruptStatus );
705     }
706 
vPortRecursiveLockRelease(BaseType_t xCoreID,BaseType_t xFromIsr)707     void vPortRecursiveLockRelease( BaseType_t xCoreID, BaseType_t xFromIsr )
708     {
709         BaseType_t xSavedInterruptStatus;
710         BaseType_t xBitPosition = ( xFromIsr == pdTRUE );
711 
712         xSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
713 
714         /* Sync memory */
715         portSYNCM();
716 
717         /* Error check whether vPortRecursiveLockRelease() is not called in
718          * pair with vPortRecursiveLockAcquire() */
719         configASSERT( ( uxLockNesting[ xCoreID ][ xBitPosition ] > 0 ) );
720         uxLockNesting[ xCoreID ][ xBitPosition ]--;
721 
722         if( uxLockNesting[ xCoreID ][ xBitPosition ] == 0 )
723         {
724             prvExclusiveRelease( xBitPosition );
725         }
726 
727         portCLEAR_INTERRUPT_MASK_FROM_ISR( xSavedInterruptStatus );
728     }
729 
730 /*-----------------------------------------------------------*/
731 
732 #endif /* (configNUMBER_OF_CORES > 1) */
733