Zynq UltraScale+ FreeRTOS: XDpPsu interrupts work, but XDpDma interrupts never reach the driver handler

Hi everyone,

I am having an issue running the AMD/Xilinx xdpdma_video_example.c with FreeRTOS on a Zynq UltraScale+ MPSoC.

Setup:

  • AUP-ZU3 / XCZU3EG
  • Vitis Unified 2025.2
  • FreeRTOS
  • PS DisplayPort + DPDMA
  • SDT flow

The original example works correctly in bare-metal: DisplayPort link training succeeds and the video is displayed.

With FreeRTOS, however, the DisplayPort initialization and link training also succeed, but nothing is displayed.

The important point is that the XDpPsu interrupts work correctly, including the HPD-related interrupts. The application enters the XDpPsu interrupt handler and the DisplayPort link is trained successfully.

However, the DPDMA interrupt never reaches:

XDpDma_InterruptHandler()

The DPDMA interrupt setup functions return XST_SUCCESS, but after enabling the interrupt I read:

DPDMA IEN = 0x00000000
ISR       = 0x00000000
IEN       = 0x00000000
IMR       = 0x07FFFFFF

So it seems that the DPDMA interrupt is either not being enabled at the peripheral level or is not reaching the GIC/ISR.

Since the XDpPsu interrupts work under the same FreeRTOS application, I suspect there may be something related to the FreeRTOS interrupt configuration or Cortex-A53 GIC integration.

Has anyone successfully used XDpDma_InterruptHandler() with FreeRTOS on Zynq UltraScale+?

Could FreeRTOS interrupt priorities, the IRQ/vector handling, or the way the Xilinx XScuGic interrupts are registered cause this behaviour?

Thanks!

I don’t know if it has been fixed, as I haven’t used the Zynq port for a while, but it used to be that the Zynq port re-initialized the GIC at scheduler start, so any interrupts that were enable before that got disabled.

Thanks, that is a useful suggestion. I checked my code, and I am using the SDT flow.

The interrupts are configured after the FreeRTOS scheduler has already started:

xTaskCreate(...);
vTaskStartScheduler();

/* Inside the task */
SetupInterrupts();

With SDT, I use XSetupInterruptSystem() for both XDpPsu and XDpDma.

The XDpPsu interrupt works correctly: HPD is detected and link training succeeds. However, XDpDma_InterruptHandler() is never called.

Also, after:

XDpDma_InterruptEnable(DpDmaPtr, XDPDMA_IEN_VSYNC_INT_MASK);

the DPDMA IEN register still reads 0x00000000.

So I don’t think the GIC is being reset after the interrupt configuration. I suspect there may be something specific to the DPDMA interrupt configuration or the SDT/FreeRTOS integration.

Thanks!

Is it possible to step through and see why the register write is not working?

Thanks for the suggestion.

We actually investigated this further. The IEN register reading back as 0x00000000 turned out not to indicate that the write failed.

AMD pointed out that DPDMA_IEN at 0xFD4C000C is a write-only (wo) register, so reading it back always returns 0x00000000, regardless of whether the write succeeded.

Instead, we checked the DPDMA_IMR register at 0xFD4C0008, which is the register that shows the interrupt mask state.

After:

XDpDma_InterruptEnable(
    DpDmaPtr,
    XDPDMA_IEN_VSYNC_INT_MASK);

we read:

DPDMA IMR = 0x07FFFFFF

The VSYNC bit is bit 27, and according to the DPDMA register definition, a cleared bit in IMR means that the interrupt is unmasked. Therefore, the VSYNC interrupt is actually enabled at the DPDMA.

We also performed an additional test by reading DPDMA_ISR (0xFD4C0004) repeatedly without clearing it. We get:

[00] DPDMA_ISR = 0x00000000
[01] DPDMA_ISR = 0x08000000
[02] DPDMA_ISR = 0x08000000
[03] DPDMA_ISR = 0x08000000
...
[19] DPDMA_ISR = 0x08000000

0x08000000 is the VSYNC interrupt bit, so this confirms that the DPDMA is actually generating/latching the VSYNC event.

Finally, we registered a simple custom ISR using the same XSetupInterruptSystem() mechanism:

XSetupInterruptSystem(
    DpDmaPtr,
    MyDpdmaIsr,
    DpDmaPtr->Config.IntrId,
    DpDmaPtr->Config.IntrParent,
    XINTERRUPT_DEFAULT_PRIORITY);

and this ISR is successfully triggered:

>>> DPDMA CPU IRQ! ISR=0x08000000 COUNT=1
>>> DPDMA CPU IRQ! ISR=0x08000000 COUNT=2
>>> DPDMA CPU IRQ! ISR=0x08000000 COUNT=3
...

So at this point, I don’t think stepping through XDpDma_InterruptEnable() is necessary. The write is reaching the hardware, as demonstrated by IMR, and the DPDMA interrupt is reaching the CPU.

The remaining issue seems to be specifically that the original XDpDma_InterruptHandler() from xdpdma_intr.c is not being called, while our custom ISR using the same interrupt ID (16506) is called correctly.

So the current question is why XDpDma_InterruptHandler() is not being dispatched under FreeRTOS/SDT even though the DPDMA interrupt itself is working.

So you are saying that when you call XSetupInterruptSystem explicitly, it works and when you call SetupInterrupts (which internally calls XSetupInterruptSystem), it does not? If so, can you check if there is any difference to the parameters passed to XSetupInterruptSystem in both the cases?