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