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