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