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