Embedded SystemsInterruptsFPGAApril 1, 2026

Event-Driven DMA: Implementing Interrupts via AXI HP Ports

Transitioning from "wait-and-watch" polling to high-performance asynchronous data movement. A deep dive into GIC configuration, ISR latency, and hardware logic inspection.

Beyond Polling

While simple polling is effective for validation, it wastes valuable CPU cycles. By integrating interrupts into the Zynq-7000 architecture, we transform the system into an event-driven powerhouse. This allows the ARM Cortex-A9 to execute complex algorithms while the AXI DMA handles the heavy lifting of moving data between the PL and DDR memory.

Required Materials

  • Hardware: Cora Z7-07S (Zynq-7000)
  • Toolchain: Xilinx Vivado + SDK 2018.3
  • Debug: Integrated Logic Analyzer (ILA) & Tera Term

DMA Interrupt & Debug Architecture

DMA Interrupt Hardware Architecture

Figure 1: Signal routing using the Concat IP to bridge DMA IRQ lines to the Zynq Processing System.

Hardware Implementation

The hardware transition requires exposing the IRQ_F2P (Fabric to Processor) port on the Zynq IP. Since the AXI DMA provides independent signals for MM2S (Memory to Stream) and S2MM (Stream to Memory), we utilize a Concat IP block to aggregate these signals into a single vector for the Generic Interrupt Controller (GIC).

1. Connect Handlers & Start GIC

static int SetupIntrSystem(INTC * IntcInstancePtr, XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
    // 1. Initialize Interrupt Controller
    XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);

    // 2. Connect Handlers (Callback functions)
    XIntc_Connect(IntcInstancePtr, TxIntrId, (XInterruptHandler) TxIntrHandler, AxiDmaPtr);
    XIntc_Connect(IntcInstancePtr, RxIntrId, (XInterruptHandler) RxIntrHandler, AxiDmaPtr);

    // 3. Start GIC and enable specific hardware lines
    XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
    XIntc_Enable(IntcInstancePtr, TxIntrId);
    XIntc_Enable(IntcInstancePtr, RxIntrId);

    // 4. Initialize Hardware Exceptions
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)INTC_HANDLER, IntcInstancePtr);
    Xil_ExceptionEnable();

    return XST_SUCCESS;
}

2. The Interrupt Service Routine (ISR)

static void TxIntrHandler(void *Callback)
{
	u32 IrqStatus;
	int TimeOut;
	XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

	/* 1. Identify the Interrupt Source */
	IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);

	/* 2. Acknowledge and Clear */
	// It is vital to acknowledge the interrupt immediately to clear 
	// the hardware latch and prepare for the next event.
	XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);

	/* 3. Filter Spurious Interrupts */
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
		return;
	}

	/* 4. Error Recovery Logic */
	// If the Error mask is set, the DMA engine has halted. 
	// We set a global error flag and initiate a hardware reset.
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
		Error = 1;
		XAxiDma_Reset(AxiDmaInst);
		TimeOut = RESET_TIMEOUT_COUNTER;

		while (TimeOut) {
			if (XAxiDma_ResetIsDone(AxiDmaInst)) {
				break;
			}
			TimeOut -= 1;
		}
		return;
	}

	/* 5. Completion Assertion */
	// XAXIDMA_IRQ_IOC_MASK indicates "Interrupt on Complete."
	// We set the volatile TxDone flag to notify the main-line code.
	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
		xil_printf("---- MM2S Interrupt is Active  ---- 
");
		TxDone = 1;
	}
}

Pro-Tip: Always use the volatile keyword for completion flags. Without it, the compiler optimizer may assume the variable never changes, causing your main loop to hang indefinitely.

Logic Inspection: The "Why" behind Latency

Using an Integrated Logic Analyzer (ILA), we can observe the micro-timing of these interrupts. A key observation in this project was the duration of the IRQ assertion.

ILA Interrupt Timing

ILA Capture: Note the gap between IRQ rise and de-assertion.

Software Latency Explained

The hardware clears the interrupt almost instantly, but the CPU takes time to finish the first ISR before it can jump to the next. In a baremetal environment, this latency is the cost of context switching and serial logging (xil_printf).

Results & Validation

---- Entering main() ---
---- MM2S Interrupt is Active ----
---- S2MM Interrupt is Active ----
Data valid 0: sent C = received C PASS
... 
Successfully ran AXI DMA interrupt Example
---- Exiting main() ---