1 /* Copyright (c) 2019, XMOS Ltd, All rights reserved */
2 
3 /* Scheduler includes. */
4 #include "FreeRTOS.h"
5 #include "task.h"
6 #include <string.h>
7 #include <xs1.h>
8 #include <xcore/hwtimer.h>
9 #include <xcore/triggerable.h>
10 
11 static hwtimer_t xKernelTimer;
12 
13 uint32_t ulPortYieldRequired[ portMAX_CORE_COUNT ] = { pdFALSE };
14 
15 /* When this port was designed, it was assumed that pxCurrentTCBs would always
16    exist and that it would always be an array containing pointers to the current
17    TCBs for each core. In v11, this is not the case; if we are only running one
18    core, the symbol is pxCurrentTCB instead. Therefore, this port adds a layer
19    of indirection - we populate this pointer-to-pointer in the RTOS kernel entry
20    function below. This makes this port agnostic to whether it is running on SMP
21    or singlecore RTOS. */
22 void ** xcorePvtTCBContainer;
23 
24 /*-----------------------------------------------------------*/
25 
vIntercoreInterruptISR(void)26 void vIntercoreInterruptISR( void )
27 {
28     int xCoreID;
29 
30 /*	debug_printf( "In KCALL: %u\n", ulData ); */
31     xCoreID = rtos_core_id_get();
32     ulPortYieldRequired[ xCoreID ] = pdTRUE;
33 }
34 /*-----------------------------------------------------------*/
35 
DEFINE_RTOS_INTERRUPT_CALLBACK(pxKernelTimerISR,pvData)36 DEFINE_RTOS_INTERRUPT_CALLBACK( pxKernelTimerISR, pvData )
37 {
38     uint32_t ulLastTrigger;
39     uint32_t ulNow;
40     int xCoreID;
41     UBaseType_t uxSavedInterruptStatus;
42 
43     xCoreID = 0;
44 
45     configASSERT( xCoreID == rtos_core_id_get() );
46 
47     /* Need the next interrupt to be scheduled relative to
48      * the current trigger time, rather than the current
49      * time. */
50     ulLastTrigger = hwtimer_get_trigger_time( xKernelTimer );
51 
52     /* Check to see if the ISR is late. If it is, we don't
53      * want to schedule the next interrupt to be in the past. */
54     ulNow = hwtimer_get_time( xKernelTimer );
55 
56     if( ulNow - ulLastTrigger >= configCPU_CLOCK_HZ / configTICK_RATE_HZ )
57     {
58         ulLastTrigger = ulNow;
59     }
60 
61     ulLastTrigger += configCPU_CLOCK_HZ / configTICK_RATE_HZ;
62     hwtimer_change_trigger_time( xKernelTimer, ulLastTrigger );
63 
64     #if configUPDATE_RTOS_TIME_FROM_TICK_ISR == 1
65         rtos_time_increment( RTOS_TICK_PERIOD( configTICK_RATE_HZ ) );
66     #endif
67 
68     uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
69 
70     if( xTaskIncrementTick() != pdFALSE )
71     {
72         ulPortYieldRequired[ xCoreID ] = pdTRUE;
73     }
74 
75     taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
76 }
77 /*-----------------------------------------------------------*/
78 
vPortYieldOtherCore(int xOtherCoreID)79 void vPortYieldOtherCore( int xOtherCoreID )
80 {
81     int xCoreID;
82 
83     /*
84      * This function must be called from within a critical section.
85      */
86 
87     xCoreID = rtos_core_id_get();
88 
89 /*	debug_printf("%d->%d\n", xCoreID, xOtherCoreID); */
90 
91 /*	debug_printf("Yield core %d from %d\n", xOtherCoreID, xCoreID ); */
92 
93     rtos_irq( xOtherCoreID, xCoreID );
94 }
95 /*-----------------------------------------------------------*/
96 
prvCoreInit(void)97 static int prvCoreInit( void )
98 {
99     int xCoreID;
100 
101     xCoreID = rtos_core_register();
102     debug_printf( "Logical Core %d initializing as FreeRTOS Core %d\n", get_logical_core_id(), xCoreID );
103 
104     asm volatile (
105         "ldap r11, kexcept\n\t"
106         "set kep, r11\n\t"
107         :
108         :
109         : "r11"
110         );
111 
112     rtos_irq_enable( configNUMBER_OF_CORES );
113 
114     /*
115      * All threads wait here until all have enabled IRQs
116      */
117     while( rtos_irq_ready() == pdFALSE )
118     {
119     }
120 
121     if( xCoreID == 0 )
122     {
123         uint32_t ulNow;
124         ulNow = hwtimer_get_time( xKernelTimer );
125 /*		debug_printf( "The time is now (%u)\n", ulNow ); */
126 
127         ulNow += configCPU_CLOCK_HZ / configTICK_RATE_HZ;
128 
129         triggerable_setup_interrupt_callback( xKernelTimer, NULL, RTOS_INTERRUPT_CALLBACK( pxKernelTimerISR ) );
130         hwtimer_set_trigger_time( xKernelTimer, ulNow );
131         triggerable_enable_trigger( xKernelTimer );
132     }
133 
134     return xCoreID;
135 }
136 /*-----------------------------------------------------------*/
137 
DEFINE_RTOS_KERNEL_ENTRY(void,vPortStartSchedulerOnCore,void)138 DEFINE_RTOS_KERNEL_ENTRY( void, vPortStartSchedulerOnCore, void )
139 {
140     int xCoreID;
141 
142     xCoreID = prvCoreInit();
143 
144     #if ( configUSE_CORE_INIT_HOOK == 1 )
145     {
146         extern void vApplicationCoreInitHook( BaseType_t xCoreID );
147 
148         vApplicationCoreInitHook( xCoreID );
149     }
150     #endif
151 
152     /* Populate the TCBContainer depending on whether we're singlecore or SMP */
153     #if ( configNUMBER_OF_CORES == 1 )
154     {
155         asm volatile (
156             "ldaw %0, dp[pxCurrentTCB]\n\t"
157             : "=r"(xcorePvtTCBContainer)
158             : /* no inputs */
159             : /* no clobbers */
160             );
161     }
162     #else
163     {
164         asm volatile (
165             "ldaw %0, dp[pxCurrentTCBs]\n\t"
166             : "=r"(xcorePvtTCBContainer)
167             : /* no inputs */
168             : /* no clobbers */
169             );
170     }
171 
172     #endif
173 
174     debug_printf( "FreeRTOS Core %d initialized\n", xCoreID );
175 
176     /*
177      * Restore the context of the first thread
178      * to run and jump into it.
179      */
180     asm volatile (
181         "mov r6, %0\n\t"                       /* R6 must be the FreeRTOS core ID. In singlecore this is always 0. */
182         "ldw r5, dp[xcorePvtTCBContainer]\n\t" /* R5 must be the TCB list which is indexed by R6 */
183         "bu _freertos_restore_ctx\n\t"
184         :                                /* no outputs */
185         : "r" ( xCoreID )
186         : "r5", "r6"
187         );
188 }
189 /*-----------------------------------------------------------*/
190 
191 /*-----------------------------------------------------------*/
192 /* Public functions required by all ports below:             */
193 /*-----------------------------------------------------------*/
194 
195 /*
196  * See header file for description.
197  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)198 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
199                                      TaskFunction_t pxCode,
200                                      void * pvParameters )
201 {
202     /*debug_printf( "Top of stack was %p for task %p\n", pxTopOfStack, pxCode ); */
203 
204     /*
205      * Grow the thread's stack by portTHREAD_CONTEXT_STACK_GROWTH
206      * so we can push the context onto it.
207      */
208     pxTopOfStack -= portTHREAD_CONTEXT_STACK_GROWTH;
209 
210     uint32_t dp;
211     uint32_t cp;
212 
213     /*
214      * We need to get the current CP and DP pointers.
215      */
216     asm volatile (
217         "ldaw r11, cp[0]\n\t"      /* get CP into R11 */
218         "mov %0, r11\n\t"          /* get R11 (CP) into cp */
219         "ldaw r11, dp[0]\n\t"      /* get DP into R11 */
220         "mov %1, r11\n\t"          /* get R11 (DP) into dp */
221         : "=r" ( cp ), "=r" ( dp ) /* output 0 is cp, output 1 is dp */
222         :                          /* there are no inputs */
223         : "r11"                    /* R11 gets clobbered */
224         );
225 
226     /*
227      * Push the thread context onto the stack.
228      * Saved PC will point to the new thread's
229      * entry pointer.
230      * Interrupts will default to enabled.
231      * KEDI is also set to enable dual issue mode
232      * upon kernel entry.
233      */
234     pxTopOfStack[ 1 ] = ( StackType_t ) pxCode;       /* SP[1]  := SPC */
235     pxTopOfStack[ 2 ] = XS1_SR_IEBLE_MASK
236                         | XS1_SR_KEDI_MASK;           /* SP[2]  := SSR */
237     pxTopOfStack[ 3 ] = 0x00000000;                   /* SP[3]  := SED */
238     pxTopOfStack[ 4 ] = 0x00000000;                   /* SP[4]  := ET */
239     pxTopOfStack[ 5 ] = dp;                           /* SP[5]  := DP */
240     pxTopOfStack[ 6 ] = cp;                           /* SP[6]  := CP */
241     pxTopOfStack[ 7 ] = 0x00000000;                   /* SP[7]  := LR */
242     pxTopOfStack[ 8 ] = ( StackType_t ) pvParameters; /* SP[8]  := R0 */
243     pxTopOfStack[ 9 ] = 0x01010101;                   /* SP[9]  := R1 */
244     pxTopOfStack[ 10 ] = 0x02020202;                  /* SP[10] := R2 */
245     pxTopOfStack[ 11 ] = 0x03030303;                  /* SP[11] := R3 */
246     pxTopOfStack[ 12 ] = 0x04040404;                  /* SP[12] := R4 */
247     pxTopOfStack[ 13 ] = 0x05050505;                  /* SP[13] := R5 */
248     pxTopOfStack[ 14 ] = 0x06060606;                  /* SP[14] := R6 */
249     pxTopOfStack[ 15 ] = 0x07070707;                  /* SP[15] := R7 */
250     pxTopOfStack[ 16 ] = 0x08080808;                  /* SP[16] := R8 */
251     pxTopOfStack[ 17 ] = 0x09090909;                  /* SP[17] := R9 */
252     pxTopOfStack[ 18 ] = 0x10101010;                  /* SP[18] := R10 */
253     pxTopOfStack[ 19 ] = 0x11111111;                  /* SP[19] := R11 */
254     pxTopOfStack[ 20 ] = 0x00000000;                  /* SP[20] := vH and vSR */
255     memset( &pxTopOfStack[ 21 ], 0, 32 );             /* SP[21 - 28] := vR   */
256     memset( &pxTopOfStack[ 29 ], 1, 32 );             /* SP[29 - 36] := vD   */
257     memset( &pxTopOfStack[ 37 ], 2, 32 );             /* SP[37 - 44] := vC   */
258 
259     /*debug_printf( "Top of stack is now %p for task %p\n", pxTopOfStack, pxCode ); */
260 
261     /*
262      * Returns the new top of the stack
263      */
264     return pxTopOfStack;
265 }
266 /*-----------------------------------------------------------*/
267 
268 void vPortStartSMPScheduler( void );
269 
270 /*
271  * See header file for description.
272  */
xPortStartScheduler(void)273 BaseType_t xPortStartScheduler( void )
274 {
275     if( ( configNUMBER_OF_CORES > portMAX_CORE_COUNT ) || ( configNUMBER_OF_CORES <= 0 ) )
276     {
277         return pdFAIL;
278     }
279 
280     rtos_locks_initialize();
281     xKernelTimer = hwtimer_alloc();
282 
283     vPortStartSMPScheduler();
284 
285     return pdPASS;
286 }
287 /*-----------------------------------------------------------*/
288 
vPortEndScheduler(void)289 void vPortEndScheduler( void )
290 {
291     /* Do not implement. */
292 }
293 /*-----------------------------------------------------------*/
294