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