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