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 MicroBlaze port.
31 *----------------------------------------------------------*/
32
33
34 /* Scheduler includes. */
35 #include "FreeRTOS.h"
36 #include "task.h"
37
38 /* Standard includes. */
39 #include <string.h>
40
41 /* Hardware includes. */
42 #include <xintc_i.h>
43 #include <xil_exception.h>
44 #include <microblaze_exceptions_g.h>
45 #include <microblaze_instructions.h>
46
47 /* Tasks are started with a critical section nesting of 0 - however, prior to
48 * the scheduler being commenced interrupts should not be enabled, so the critical
49 * nesting variable is initialised to a non-zero value. */
50 #define portINITIAL_NESTING_VALUE ( 0xff )
51
52 /* The bit within the MSR register that enabled/disables interrupts and
53 * exceptions respectively. */
54 #define portMSR_IE ( 0x02U )
55 #define portMSR_EE ( 0x100U )
56
57 /* If the floating point unit is included in the MicroBlaze build, then the
58 * FSR register is saved as part of the task context. portINITIAL_FSR is the value
59 * given to the FSR register when the initial context is set up for a task being
60 * created. */
61 #define portINITIAL_FSR ( 0U )
62 /*
63 * Global counter used for calculation of run time statistics of tasks.
64 * Defined only when the relevant option is turned on
65 */
66 #if (configGENERATE_RUN_TIME_STATS==1)
67 volatile uint32_t ulHighFrequencyTimerTicks;
68 #endif
69
70 /*-----------------------------------------------------------*/
71
72 /*
73 * Initialise the interrupt controller instance.
74 */
75 static int32_t prvInitialiseInterruptController( void );
76
77 /* Ensure the interrupt controller instance variable is initialised before it is
78 * used, and that the initialisation only happens once.
79 */
80 static int32_t prvEnsureInterruptControllerIsInitialised( void );
81
82 /*-----------------------------------------------------------*/
83
84 /* Counts the nesting depth of calls to portENTER_CRITICAL(). Each task
85 * maintains its own count, so this variable is saved as part of the task
86 * context. */
87 volatile UBaseType_t uxCriticalNesting = portINITIAL_NESTING_VALUE;
88
89 /* This port uses a separate stack for interrupts. This prevents the stack of
90 * every task needing to be large enough to hold an entire interrupt stack on top
91 * of the task stack. */
92 uint32_t * pulISRStack;
93
94 /* If an interrupt requests a context switch, then ulTaskSwitchRequested will
95 * get set to 1. ulTaskSwitchRequested is inspected just before the main interrupt
96 * handler exits. If, at that time, ulTaskSwitchRequested is set to 1, the kernel
97 * will call vTaskSwitchContext() to ensure the task that runs immediately after
98 * the interrupt exists is the highest priority task that is able to run. This is
99 * an unusual mechanism, but is used for this port because a single interrupt can
100 * cause the servicing of multiple peripherals - and it is inefficient to call
101 * vTaskSwitchContext() multiple times as each peripheral is serviced. */
102 volatile uint32_t ulTaskSwitchRequested = 0UL;
103
104 /* The instance of the interrupt controller used by this port. This is required
105 * by the Xilinx library API functions. */
106 static XIntc xInterruptControllerInstance;
107
108 /*-----------------------------------------------------------*/
109
110 /*
111 * Initialise the stack of a task to look exactly as if a call to
112 * portSAVE_CONTEXT had been made.
113 *
114 * See the portable.h header file.
115 */
116 #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
pxPortInitialiseStack(StackType_t * pxTopOfStack,StackType_t * pxEndOfStack,TaskFunction_t pxCode,void * pvParameters)117 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
118 StackType_t * pxEndOfStack,
119 TaskFunction_t pxCode,
120 void * pvParameters )
121 #else
122 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
123 TaskFunction_t pxCode,
124 void * pvParameters )
125 #endif
126 {
127 extern void *_SDA2_BASE_;
128 extern void *_SDA_BASE_;
129 const UINTPTR ulR2 = ( UINTPTR ) &_SDA2_BASE_;
130 const UINTPTR ulR13 = ( UINTPTR ) &_SDA_BASE_;
131
132 extern void _start1( void );
133
134 /* Place a few bytes of known values on the bottom of the stack.
135 * This is essential for the Microblaze port and these lines must
136 * not be omitted. */
137 *pxTopOfStack = ( StackType_t ) 0x00000000;
138 pxTopOfStack--;
139 *pxTopOfStack = ( StackType_t ) 0x00000000;
140 pxTopOfStack--;
141 *pxTopOfStack = ( StackType_t ) 0x00000000;
142 pxTopOfStack--;
143
144 #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
145 /* Store the stack limits. */
146 *pxTopOfStack = ( StackType_t ) ( pxTopOfStack + 3 );
147 pxTopOfStack--;
148 *pxTopOfStack = ( StackType_t ) pxEndOfStack;
149 pxTopOfStack--;
150 #endif
151
152 #if ( XPAR_MICROBLAZE_USE_FPU != 0 )
153 /* The FSR value placed in the initial task context is just 0. */
154 *pxTopOfStack = portINITIAL_FSR;
155 pxTopOfStack--;
156 #endif
157
158 /* The MSR value placed in the initial task context should have interrupts
159 * disabled. Each task will enable interrupts automatically when it enters
160 * the running state for the first time. */
161 *pxTopOfStack = mfmsr() & ~portMSR_IE;
162
163 #if ( MICROBLAZE_EXCEPTIONS_ENABLED == 1 )
164 {
165 /* Ensure exceptions are enabled for the task. */
166 *pxTopOfStack |= portMSR_EE;
167 }
168 #endif
169
170 pxTopOfStack--;
171
172 /* First stack an initial value for the critical section nesting. This
173 * is initialised to zero. */
174 *pxTopOfStack = ( StackType_t ) 0x00;
175
176 /* R0 is always zero. */
177 /* R1 is the SP. */
178
179 /* Place an initial value for all the general purpose registers. */
180 pxTopOfStack--;
181 *pxTopOfStack = ( StackType_t ) ulR2; /* R2 - read only small data area. */
182 pxTopOfStack--;
183 *pxTopOfStack = ( StackType_t ) 0x03; /* R3 - return values and temporaries. */
184 pxTopOfStack--;
185 *pxTopOfStack = ( StackType_t ) 0x04; /* R4 - return values and temporaries. */
186 pxTopOfStack--;
187 *pxTopOfStack = ( StackType_t ) pvParameters; /* R5 contains the function call parameters. */
188
189 #ifdef portPRE_LOAD_STACK_FOR_DEBUGGING
190 pxTopOfStack--;
191 *pxTopOfStack = ( StackType_t ) 0x06; /* R6 - other parameters and temporaries. */
192 pxTopOfStack--;
193 *pxTopOfStack = ( StackType_t ) 0x07; /* R7 - other parameters and temporaries. */
194 pxTopOfStack--;
195 *pxTopOfStack = ( StackType_t ) NULL; /* R8 - other parameters and temporaries. */
196 pxTopOfStack--;
197 *pxTopOfStack = ( StackType_t ) 0x09; /* R9 - other parameters and temporaries. */
198 pxTopOfStack--;
199 *pxTopOfStack = ( StackType_t ) 0x0a; /* R10 - other parameters and temporaries. */
200 pxTopOfStack--;
201 *pxTopOfStack = ( StackType_t ) 0x0b; /* R11 - temporaries. */
202 pxTopOfStack--;
203 *pxTopOfStack = ( StackType_t ) 0x0c; /* R12 - temporaries. */
204 pxTopOfStack--;
205 #else /* ifdef portPRE_LOAD_STACK_FOR_DEBUGGING */
206 pxTopOfStack -= 8;
207 #endif /* ifdef portPRE_LOAD_STACK_FOR_DEBUGGING */
208
209 *pxTopOfStack = ( StackType_t ) ulR13; /* R13 - read/write small data area. */
210 pxTopOfStack--;
211 *pxTopOfStack = ( StackType_t ) pxCode; /* R14 - return address for interrupt. */
212 pxTopOfStack--;
213 *pxTopOfStack = ( StackType_t ) _start1; /* R15 - return address for subroutine. */
214
215 #ifdef portPRE_LOAD_STACK_FOR_DEBUGGING
216 pxTopOfStack--;
217 *pxTopOfStack = ( StackType_t ) 0x10; /* R16 - return address for trap (debugger). */
218 pxTopOfStack--;
219 *pxTopOfStack = ( StackType_t ) 0x11; /* R17 - return address for exceptions, if configured. */
220 pxTopOfStack--;
221 *pxTopOfStack = ( StackType_t ) 0x12; /* R18 - reserved for assembler and compiler temporaries. */
222 pxTopOfStack--;
223 #else
224 pxTopOfStack -= 4;
225 #endif
226
227 *pxTopOfStack = ( StackType_t ) 0x00; /* R19 - must be saved across function calls. Callee-save. Seems to be interpreted as the frame pointer. */
228
229 #ifdef portPRE_LOAD_STACK_FOR_DEBUGGING
230 pxTopOfStack--;
231 *pxTopOfStack = ( StackType_t ) 0x14; /* R20 - reserved for storing a pointer to the Global Offset Table (GOT) in Position Independent Code (PIC). Non-volatile in non-PIC code. Must be saved across function calls. Callee-save. Not used by FreeRTOS. */
232 pxTopOfStack--;
233 *pxTopOfStack = ( StackType_t ) 0x15; /* R21 - must be saved across function calls. Callee-save. */
234 pxTopOfStack--;
235 *pxTopOfStack = ( StackType_t ) 0x16; /* R22 - must be saved across function calls. Callee-save. */
236 pxTopOfStack--;
237 *pxTopOfStack = ( StackType_t ) 0x17; /* R23 - must be saved across function calls. Callee-save. */
238 pxTopOfStack--;
239 *pxTopOfStack = ( StackType_t ) 0x18; /* R24 - must be saved across function calls. Callee-save. */
240 pxTopOfStack--;
241 *pxTopOfStack = ( StackType_t ) 0x19; /* R25 - must be saved across function calls. Callee-save. */
242 pxTopOfStack--;
243 *pxTopOfStack = ( StackType_t ) 0x1a; /* R26 - must be saved across function calls. Callee-save. */
244 pxTopOfStack--;
245 *pxTopOfStack = ( StackType_t ) 0x1b; /* R27 - must be saved across function calls. Callee-save. */
246 pxTopOfStack--;
247 *pxTopOfStack = ( StackType_t ) 0x1c; /* R28 - must be saved across function calls. Callee-save. */
248 pxTopOfStack--;
249 *pxTopOfStack = ( StackType_t ) 0x1d; /* R29 - must be saved across function calls. Callee-save. */
250 pxTopOfStack--;
251 *pxTopOfStack = ( StackType_t ) 0x1e; /* R30 - must be saved across function calls. Callee-save. */
252 pxTopOfStack--;
253 *pxTopOfStack = ( StackType_t ) 0x1f; /* R31 - must be saved across function calls. Callee-save. */
254 pxTopOfStack--;
255 #else /* ifdef portPRE_LOAD_STACK_FOR_DEBUGGING */
256 pxTopOfStack -= 13;
257 #endif /* ifdef portPRE_LOAD_STACK_FOR_DEBUGGING */
258
259 /* Return a pointer to the top of the stack that has been generated so this
260 * can be stored in the task control block for the task. */
261 return pxTopOfStack;
262 }
263 /*-----------------------------------------------------------*/
264
xPortStartScheduler(void)265 BaseType_t xPortStartScheduler( void )
266 {
267 extern void ( vPortStartFirstTask )( void );
268 extern UINTPTR _stack[];
269
270 /* Setup the hardware to generate the tick. Interrupts are disabled when
271 * this function is called.
272 *
273 * This port uses an application defined callback function to install the tick
274 * interrupt handler because the kernel will run on lots of different
275 * MicroBlaze and FPGA configurations - not all of which will have the same
276 * timer peripherals defined or available. An example definition of
277 * vApplicationSetupTimerInterrupt() is provided in the official demo
278 * application that accompanies this port. */
279 vApplicationSetupTimerInterrupt();
280
281 /* Reuse the stack from main() as the stack for the interrupts/exceptions. */
282 pulISRStack = ( UINTPTR * ) _stack;
283
284 /* Ensure there is enough space for the functions called from the interrupt
285 * service routines to write back into the stack frame of the caller. */
286 pulISRStack -= 2;
287
288 /* Restore the context of the first task that is going to run. From here
289 * on, the created tasks will be executing. */
290 vPortStartFirstTask();
291
292 /* Should not get here as the tasks are now running! */
293 return pdFALSE;
294 }
295 /*-----------------------------------------------------------*/
296
vPortEndScheduler(void)297 void vPortEndScheduler( void )
298 {
299 /* Not implemented in ports where there is nothing to return to.
300 * Artificially force an assert. */
301 configASSERT( uxCriticalNesting == 1000UL );
302 }
303 /*-----------------------------------------------------------*/
304
305 /*
306 * Manual context switch called by portYIELD or taskYIELD.
307 */
vPortYield(void)308 void vPortYield( void )
309 {
310 extern void VPortYieldASM( void );
311
312 /* Perform the context switch in a critical section to assure it is
313 * not interrupted by the tick ISR. It is not a problem to do this as
314 * each task maintains its own interrupt status. */
315 portENTER_CRITICAL();
316 {
317 /* Jump directly to the yield function to ensure there is no
318 * compiler generated prologue code. */
319 #ifdef __arch64__
320 asm volatile ( "brealid r14, VPortYieldASM \n\t" \
321 "or r0, r0, r0 \n\t" );
322 #else
323 asm volatile ( "bralid r14, VPortYieldASM \n\t" \
324 "or r0, r0, r0 \n\t" );
325 #endif
326 }
327 portEXIT_CRITICAL();
328 }
329 /*-----------------------------------------------------------*/
330
vPortEnableInterrupt(uint8_t ucInterruptID)331 void vPortEnableInterrupt( uint8_t ucInterruptID )
332 {
333 int32_t lReturn;
334
335 /* An API function is provided to enable an interrupt in the interrupt
336 * controller because the interrupt controller instance variable is private
337 * to this file. */
338 lReturn = prvEnsureInterruptControllerIsInitialised();
339
340 if( lReturn == pdPASS )
341 {
342 /* Critical section protects read/modify/writer operation inside
343 * XIntc_Enable(). */
344 portENTER_CRITICAL();
345 {
346 XIntc_Enable( &xInterruptControllerInstance, ucInterruptID );
347 }
348 portEXIT_CRITICAL();
349 }
350
351 configASSERT( lReturn == pdPASS );
352 }
353 /*-----------------------------------------------------------*/
354
vPortDisableInterrupt(uint8_t ucInterruptID)355 void vPortDisableInterrupt( uint8_t ucInterruptID )
356 {
357 int32_t lReturn;
358
359 /* An API function is provided to disable an interrupt in the interrupt
360 * controller because the interrupt controller instance variable is private
361 * to this file. */
362 lReturn = prvEnsureInterruptControllerIsInitialised();
363
364 if( lReturn == pdPASS )
365 {
366 XIntc_Disable( &xInterruptControllerInstance, ucInterruptID );
367 }
368
369 configASSERT( lReturn == pdPASS );
370 }
371 /*-----------------------------------------------------------*/
372
xPortInstallInterruptHandler(uint8_t ucInterruptID,XInterruptHandler pxHandler,void * pvCallBackRef)373 BaseType_t xPortInstallInterruptHandler( uint8_t ucInterruptID,
374 XInterruptHandler pxHandler,
375 void * pvCallBackRef )
376 {
377 int32_t lReturn;
378
379 /* An API function is provided to install an interrupt handler because the
380 * interrupt controller instance variable is private to this file. */
381
382 lReturn = prvEnsureInterruptControllerIsInitialised();
383
384 if( lReturn == pdPASS )
385 {
386 lReturn = XIntc_Connect( &xInterruptControllerInstance, ucInterruptID, pxHandler, pvCallBackRef );
387 }
388
389 if( lReturn == XST_SUCCESS )
390 {
391 lReturn = pdPASS;
392 }
393
394 configASSERT( lReturn == pdPASS );
395
396 return lReturn;
397 }
398 /*-----------------------------------------------------------*/
399
vPortRemoveInterruptHandler(uint8_t ucInterruptID)400 void vPortRemoveInterruptHandler( uint8_t ucInterruptID )
401 {
402 int32_t lReturn;
403
404 /* An API function is provided to remove an interrupt handler because the
405 * interrupt controller instance variable is private to this file. */
406
407 lReturn = prvEnsureInterruptControllerIsInitialised();
408
409 if( lReturn == pdPASS )
410 {
411 XIntc_Disconnect( &xInterruptControllerInstance, ucInterruptID );
412 }
413
414 configASSERT( lReturn == pdPASS );
415 }
416 /*-----------------------------------------------------------*/
417
prvEnsureInterruptControllerIsInitialised(void)418 static int32_t prvEnsureInterruptControllerIsInitialised( void )
419 {
420 static int32_t lInterruptControllerInitialised = pdFALSE;
421 int32_t lReturn;
422
423 /* Ensure the interrupt controller instance variable is initialised before
424 * it is used, and that the initialisation only happens once. */
425 if( lInterruptControllerInitialised != pdTRUE )
426 {
427 lReturn = prvInitialiseInterruptController();
428
429 if( lReturn == pdPASS )
430 {
431 lInterruptControllerInitialised = pdTRUE;
432 }
433 }
434 else
435 {
436 lReturn = pdPASS;
437 }
438
439 return lReturn;
440 }
441 /*-----------------------------------------------------------*/
442
443 /*
444 * Handler for the timer interrupt. This is the handler that the application
445 * defined callback function vApplicationSetupTimerInterrupt() should install.
446 */
vPortTickISR(void * pvUnused)447 void vPortTickISR( void * pvUnused )
448 {
449 extern void vApplicationClearTimerInterrupt( void );
450
451 /* Ensure the unused parameter does not generate a compiler warning. */
452 ( void ) pvUnused;
453
454 /* The Xilinx implementation of generating run time task stats uses the same timer used for generating
455 * FreeRTOS ticks. In case user decides to generate run time stats the tick handler is called more
456 * frequently (10 times faster). The timer ick handler uses logic to handle the same. It handles
457 * the FreeRTOS tick once per 10 interrupts.
458 * For handling generation of run time stats, it increments a pre-defined counter every time the
459 * interrupt handler executes. */
460 #if (configGENERATE_RUN_TIME_STATS == 1)
461 ulHighFrequencyTimerTicks++;
462 if (!(ulHighFrequencyTimerTicks % 10))
463 #endif
464 {
465 /* This port uses an application defined callback function to clear the tick
466 * interrupt because the kernel will run on lots of different MicroBlaze and
467 * FPGA configurations - not all of which will have the same timer peripherals
468 * defined or available. An example definition of
469 * vApplicationClearTimerInterrupt() is provided in the official demo
470 * application that accompanies this port. */
471 vApplicationClearTimerInterrupt();
472
473 /* Increment the RTOS tick - this might cause a task to unblock. */
474 if( xTaskIncrementTick() != pdFALSE )
475 {
476 /* Force vTaskSwitchContext() to be called as the interrupt exits. */
477 ulTaskSwitchRequested = 1;
478 }
479 }
480 }
481 /*-----------------------------------------------------------*/
482
prvInitialiseInterruptController(void)483 static int32_t prvInitialiseInterruptController( void )
484 {
485 int32_t lStatus;
486
487 lStatus = XIntc_Initialize( &xInterruptControllerInstance, configINTERRUPT_CONTROLLER_TO_USE );
488
489 if( lStatus == XST_SUCCESS )
490 {
491 /* Initialise the exception table. */
492 Xil_ExceptionInit();
493
494 /* Service all pending interrupts each time the handler is entered. */
495 XIntc_SetIntrSvcOption( xInterruptControllerInstance.BaseAddress, XIN_SVC_ALL_ISRS_OPTION );
496
497 /* Install exception handlers if the MicroBlaze is configured to handle
498 * exceptions, and the application defined constant
499 * configINSTALL_EXCEPTION_HANDLERS is set to 1. */
500 #if ( MICROBLAZE_EXCEPTIONS_ENABLED == 1 ) && ( configINSTALL_EXCEPTION_HANDLERS == 1 )
501 {
502 vPortExceptionsInstallHandlers();
503 }
504 #endif /* MICROBLAZE_EXCEPTIONS_ENABLED */
505
506 /* Start the interrupt controller. Interrupts are enabled when the
507 * scheduler starts. */
508 lStatus = XIntc_Start( &xInterruptControllerInstance, XIN_REAL_MODE );
509
510 if( lStatus == XST_SUCCESS )
511 {
512 lStatus = pdPASS;
513 }
514 else
515 {
516 lStatus = pdFAIL;
517 }
518 }
519
520 configASSERT( lStatus == pdPASS );
521
522 return lStatus;
523 }
524
525 #if( configGENERATE_RUN_TIME_STATS == 1 )
526 /*
527 * For Xilinx implementation this is a dummy function that does a redundant operation
528 * of zeroing out the global counter.
529 * It is called by FreeRTOS kernel.
530 */
xCONFIGURE_TIMER_FOR_RUN_TIME_STATS(void)531 void xCONFIGURE_TIMER_FOR_RUN_TIME_STATS (void)
532 {
533 ulHighFrequencyTimerTicks = 0;
534 }
535 /*
536 * For Xilinx implementation this function returns the global counter used for
537 * run time task stats calculation.
538 * It is called by FreeRTOS kernel task handling logic.
539 */
xGET_RUN_TIME_COUNTER_VALUE(void)540 uint32_t xGET_RUN_TIME_COUNTER_VALUE (void)
541 {
542 return ulHighFrequencyTimerTicks;
543 }
544 #endif
545 /*-----------------------------------------------------------*/
546