Zynq 7020 interrupts under FreeRTOS v10.1.1

Hello,

I have just started to work with the Zynq 7020 SoC. I have got one FreeRTOS task up and running and now I would like to implement interrupt comming from the PL. I have searched through this forum and I have found that whenever I want to use interrupts along with the FreeRTOS I have to use the xInterruptController instance of GIC defined in the portZynq7000.c module. It make sense to me because also the FreeRTOS needs interrupts for starting its scheduler. Unfortunately I am not sure what should be the next steps necessary to get interrupts under FreeRTOS up and running.

Based on what I have mentioned above I guess that I have to ommit calls of functions XScuGic_LookupConfig and XScuGic_CfgInitialize because both of them have been already called by the FreeRTOS_SetupTickInterrupt function defined in portZynq7000.c module. I have to call only the XScuGic_Connect and the XScuGic_Enable functions to append my interrupt to the existing GIC instance. Is that correct?

If I will continue with comparison between usage of interrupts on bare metal and interrupts usage under FreeRTOS I guess that in the second case I can also ommit the Xil_ExceptioRegisterHandler and Xil_ExceptionEnable function calls. Is that correct?

In case the above mentioned settings is correct I should be able to observe IRQ in spi_status_0 or spi_status_1 register based on IRQ ID. Is that correct?

Thanks in advance for your help.

There is important information there: https://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html - and you will find some worked examples here https://github.com/FreeRTOS/FreeRTOS/blob/V10.2.1-convergence/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/Full_Demo/IntQueueTimer.c

Hello Richard Barry,

thank you for your answer. I have just inspected both of the links you have sent me. Based on that I would say that:

Answer to my first question is Yes.

As far as my second question I think that the answer is also Yes. Unfortunately I don´t understand why I can ommit the Xil_ExceptioRegisterHandler and Xil_ExceptionEnable function calls. My understanding is that via these functions I configure the interrupt interface in the ARM Cortex A9 core. I have attempted to find where in the FreeRTOS port source code these functions are called and I haven´t found nothing. It is confusing for me because I expected that these functions have to be called somewhere. Please can you tell me how does it work? Thank you.

As far as my third question I have implemented the interrupt comming from the PL. Program execution jumps into associated interrupt service routine but in case I observe content of the spi_status_0 register (my interrupt has 61 ID) in debugger (memory address 0xF8F01D04) I see still only zeros.

Hi @Steve! Since I work with Zynq Ultrascale+ devices and the FreeRTOS port from xilinx, I feel like I can answer correctly to your question. Here it is :

When using freertos as opposed to bare metal, you don’t have to connect the IRQ exception. Remember, connecting an interrupt handler to an IRQ number is not the same thing as connecting an exception handler to an exception source. The processor has only a few EXCEPTION sources, where it has numerous IRQ sources. For the cortex R5 found on zynq ultrascale+ (you use 7000 devices, so there are no R5 but two A something, but the idea is the same) You have 6 exception sources, namely ‘Data abort’, ‘Prefetch Abort’, ‘Undefined Abort’, ‘IRQ’, ‘Service Call’ and ‘FIQ’. There are two others : ‘break point’ and ‘Reset’, but you dont worry about those two.

The GIC is physically connected to the IRQ line of the processor, to which are connected the peripheral irq lines. With FreeRTOS on zynq, you don’t have to connect the FreeRTOS_IRQ_Exception_Handler to the IRQ Exception because it’s already done in the assembly file. BUT, you can connect custom C functions to the 3 aborts (data,prefetch and undefined) in order to print more debug info than a simple xil_printf saying an abort occured.

The functions XScuGic_LookupConfig and XScuGic_CfgInitialize are called automatically when the scheduler is started and it will configure the xInterruptController struct appropriately. Now, there is a gotcha with this. If, before starting the scheduler, you try to initialize some peripherals and use the functions XScuGic_Connect with the xInterruptController struct, then you will get asserts because this global struct will be uninitialized. So there are two options :

1 - Only touch the xInterruptController struct AFTER the scheduler is started. This can be conter-intuitive because you need your peripherals initialized. There is a workaround to this : there is a #define you can define and it will call a ‘hook’ that is called right after the scheduler is started, but BEFORE task start to switch. You can initialize stuff in there.

2 - Initialyze yourselfe the struct xInterruptController with the functions XScuGic_LookupConfig and XScuGic_CfgInitialize.

3 - The more elegant way : Use the provided initialization function defined in the portmacro.h : xPortInstallInterruptHandler. That function, when called the first time, will initialize the xInterruptController struct the same way as solution #2, but in a ‘clean way’. With this solution, you don’t have to use the xInterruptController struct declared in the portZynqUltrascale.c (in my case) file (you have to declare it ‘extern’ in your code in order to be sure to use the same struct as freertos port code)

In the same fashion, in order to enable or disable interrupts, you can use the functions vPortEnableInterrupt and vPortDisableInterrupt that will in background use the xInterruptController struct from port.c

In my opition, use the option #3. It’s not very documented. When reading exemple code from xilinx, people are tempted to mimic the standalone code and use a XScuGic xInterruptController

I hope it clarifies the situation.

@Steve Again, I’m talking for the CortexR5 on ZynqUltrascale+ device, but it must be verrrry close to the zynq7000. Take a look at the file port_asm_vector.S in the BSP. You will find this :

_vector_table:
ldr pc,=_boot
ldr pc,=UndefinedHandler
ldr pc, _swi
ldr pc,=PrefetchAbortHandler
ldr pc,=DataAbortHandler
NOP /* Placeholder for address exception vector*/
ldr pc, _irq
ldr pc,=FIQHandler

_irq: .word FreeRTOS_IRQ_Handler
_swi: .word FreeRTOS_SWI_Handler

FreeRTOS_IRQ_Handler and FreeRTOS_SWI_Handler are ASM functions declared in portASM.S. They are not meant to be toyed with inside your code : their addresses are already placed inside the exception vector table for you at compile time. On the other hand, UndefinedHandler, PrefetchAbortHandler, DataAbortHandler and FIQHandler are global variables that hold addresses of C functions meant to be changed by calls to Xil_ExceptioRegisterHandler.