ZYNQ multi interrupt control

Keywords: Embedded system Single-Chip Microcomputer zynq

preface

I found that many explanations are about how to realize the control of a single interrupt, but there is basically no explanation of multiple interrupts.

ZYNQ interrupt

Interrupt classification

Interrupts are divided into three categories
1) PPI private interrupt
2) SGI software interrupt
3) SPI share interrupt

PPI private interrupt

Each CPU has private interrupts. PPI includes global timer, private supervision timer, private timer and FIQ/IRQ from PL. The following table is the ID of PPI:

SGI software interrupt

The software generated interrupt is routed to one or two CPU s. The following table shows the interrupt ID of SGI:

SPI share interrupt

Shared peripheral interrupts (SPI) are generated by various I/O and memory controllers in PS and PL, which are routed to one or two CPUs. SPI interrupts from PS peripherals are also routed to pl. The following table shows the interrupt ID of SPI:

GIC general interrupt controller

The general interrupt controller (GIC) is a centralized resource for managing interrupts sent from PS and PL to the CPU. The controller enables, disables, masks and prioritizes the interrupt source, and sends them programmatically to the selected CPU (or CPU) when the CPU interface accepts the next interrupt. In addition, the controller supports the security extension of the security aware system.

From the interrupt structure diagram, it can be roughly understood that interrupts can be processed by different CPU s. How the code is implemented is in the interrupt initialization code.

give an example

Use the DMA interrupt and port of PS terminal to accept the interrupt

The basic theoretical knowledge of DMA and serial port will not be explained, which is not the focus of this article

Basic configuration

PS_UART initialization and interrupt initialization

/*
 * uart.h
 *
 * Created on: 2021 November 19
 * Author: heiheiの
 */

#ifndef SRC_UART_H_
#define SRC_UART_H_
#include "xstatus.h"
#include "xuartps.h"
#include "xscugic.h"
#include "stdio.h"
#define UART_DEVICE_ID     XPAR_PS7_UART_0_DEVICE_ID     	// Serial port device ID
#define INTC_DEVICE_ID     XPAR_SCUGIC_SINGLE_DEVICE_ID  	// Interrupt ID
#define UART_INT_IRQ_ID    XPAR_XUARTPS_0_INTR           	// Serial port interrupt ID

#define BAUD_UARTPS  115200

#define BUFFER_SIZE 8
#define BUFFER_SZE 100
extern u8 SendBuffer[BUFFER_SZE];
extern u8 RecvBuffer[BUFFER_SZE];

int Uart_Init(XUartPs *UartInstPtr);
int Uart_Intr_Init(XScuGic *IntcInstancePtr,XUartPs *UartInstPtr);

void uart_intr_handler(void *call_back_ref);

#endif /* SRC_UART_H_ */
/*
 * uart.c
 *
 *  Created on: 2021 November 19
 *      Author: heiheiの
 */
#include "uart.h"
#include "ps_dma.h"
#include "sleep.h"

extern u8 uart_send[512];
extern u8 recv_total_byte;

int Uart_Init(XUartPs *UartInstPtr){
	XUartPs_Config *Config;
	int status;

	//Get device base address
	Config = XUartPs_LookupConfig(UART_DEVICE_ID);
		if (NULL == Config) {
			return XST_FAILURE;
		}
	//Device driver instance initialization
	status = XUartPs_CfgInitialize(UartInstPtr, Config, Config->BaseAddress);
		if (status != XST_SUCCESS) {
			printf("Config Uart fail\r\n");
				return XST_FAILURE;
		}
	status=XUartPs_SelfTest(UartInstPtr);
	if (status != XST_SUCCESS) {
		print("Self test Fail\r\n");
					return XST_FAILURE;
			}
	//set baud rate
	XUartPs_SetBaudRate(UartInstPtr,BAUD_UARTPS);
	//Setting mode
	XUartPs_SetOperMode(UartInstPtr,XUARTPS_OPER_MODE_NORMAL);

	XUartPs_SetFifoThreshold(UartInstPtr,32);

	XUartPs_SetRecvTimeout(UartInstPtr,4);

	XUartPs_SetInterruptMask(UartInstPtr, XUARTPS_IXR_RXOVR|XUARTPS_IXR_TOUT);

	return XST_SUCCESS;
}

int Uart_Intr_Init(XScuGic *IntcInstancePtr,XUartPs *UartInstPtr){

	u32 status;

	XScuGic_Config *IntcConfig;

	XScuGic_Disable(IntcInstancePtr,UART_INT_IRQ_ID);

	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);

	status=XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
						IntcConfig->CpuBaseAddress);

	//Register exception callback function
	Xil_ExceptionInit();

	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
					(Xil_ExceptionHandler) XScuGic_InterruptHandler,
					IntcInstancePtr);
	//Enable exception callback
	Xil_ExceptionEnable();
	//Set serial port receive interrupt priority
	XScuGic_SetPriorityTriggerType(IntcInstancePtr,UART_INT_IRQ_ID,32,1);

	XScuGic_Connect(IntcInstancePtr, UART_INT_IRQ_ID,
					  (Xil_ExceptionHandler)uart_intr_handler,
					  (void *) UartInstPtr);

	XScuGic_Enable(IntcInstancePtr,UART_INT_IRQ_ID);

	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);

	return XST_SUCCESS;
}

void uart_intr_handler(void *call_back_ref)
{

	XUartPs *InstancePtr=(XUartPs *)call_back_ref;

	u32 IsrStatus;

	u32 ReceivedCount;

	IsrStatus = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
            XUARTPS_IMR_OFFSET);

	IsrStatus &= XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
	                   XUARTPS_ISR_OFFSET);

	if (IsrStatus & ((u32)XUARTPS_IXR_RXOVR)){

		XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR) ;
		ReceivedCount=XUartPs_Recv(InstancePtr,&uart_send[recv_total_byte],500);
		recv_total_byte+=ReceivedCount;
		printf("uart_send1:%s\r\n",uart_send);

	}
	else if(IsrStatus & ((u32)XUARTPS_IXR_TOUT)){
		XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_ISR_OFFSET, XUARTPS_IXR_TOUT) ;
		ReceivedCount=XUartPs_Recv(InstancePtr,&uart_send[recv_total_byte],500);

		recv_total_byte+=ReceivedCount;
		printf("uart_send1:%d\r\n",ReceivedCount);
		for(int i=0;i<recv_total_byte;i++)
			XUartPs_SendByte(STDOUT_BASEADDRESS,uart_send[i]);

		recv_total_byte=0;
	}
}

DMA initialization and interrupt functions

/*
 * ps_dam.h
 *
 *  Created on: 2021 November 17
 *      Author: heiheiの
 */

#ifndef SRC_PS_DMA_H_
#define SRC_PS_DMA_H_
#include "xdmaps.h"
#include <stdlib.h>
#include "xscugic.h"
#include "xuartps_hw.h"
#include <stdio.h>

#define DMA_DEVIEC_ID 			XPAR_XDMAPS_1_DEVICE_ID
#define DMA_INT_DEVIEC_ID 	    XPAR_SCUGIC_SINGLE_DEVICE_ID

#define DMA0_INT_ID				XPS_DMA0_INT_ID
#define DMA1_INT_ID				XPS_DMA1_INT_ID
#define DMA2_INT_ID				XPS_DMA2_INT_ID
#define DMA3_INT_ID				XPS_DMA3_INT_ID

#define DMA4_INT_ID				XPS_DMA4_INT_ID
#define DMA5_INT_ID				XPS_DMA5_INT_ID
#define DMA6_INT_ID				XPS_DMA6_INT_ID
#define DMA7_INT_ID				XPS_DMA7_INT_ID
#define DMA_FAULT_INTR			XPAR_XDMAPS_0_FAULT_INTR

#define DMA_LENGTH 				5

int PsDMA_Init(XDmaPs *DmaInstance,XDmaPs_Cmd *DmaCmd,void *Src,void *Dst);
int PsDMA_Intr_Init(XDmaPs *DmaInstance,XScuGic *GicPtr);
void IntcTypeSetup(XScuGic *InstancePtr,int intld,int intType);

#endif /* SRC_PS_DMA_H_ */

/*
 * ps_dma.c
 *
 *  Created on: 2021 November 17
 *      Author: heiheiの
 */
 
#include "ps_dma.h"

int PsDMA_Init(XDmaPs *DmaInstance,XDmaPs_Cmd *DmaCmd,void *Src,void *Dst)
{
	XDmaPs_Config *DmaCfg;
	u32 status=XST_SUCCESS;
	memset(DmaCmd,0,sizeof(XDmaPs_Cmd));
	DmaCmd->ChanCtrl.DstBurstLen=1; 	//Source release
	DmaCmd->ChanCtrl.DstBurstSize=1;	//Source release length
	DmaCmd->ChanCtrl.DstInc=1;			//Incremental or fixed address of the source
	DmaCmd->ChanCtrl.SrcBurstLen=1;		// Target release
	DmaCmd->ChanCtrl.SrcBurstSize=1;	// Destination release length
	DmaCmd->ChanCtrl.SrcInc=1;			// Destination release incremental or fixed address

	DmaCmd->BD.SrcAddr=((u32)(Src));
	DmaCmd->BD.DstAddr=((u32)Dst);

	DmaCmd->BD.Length=4;

	DmaCfg=XDmaPs_LookupConfig(DMA_DEVIEC_ID);
	if(DmaCfg == NULL)
		return XST_FAILURE;

	status=XDmaPs_CfgInitialize(DmaInstance,DmaCfg,DmaCfg->BaseAddress);
	if(status != XST_SUCCESS)
		return XST_FAILURE;

	return XST_SUCCESS;
}

int PsDMA_Intr_Init(XDmaPs *DmaInstance,XScuGic *GicPtr){
	u32 status=XST_SUCCESS;
	XScuGic_Config *GicConfig;

	Xil_ExceptionInit();
	//Device initialization
	GicConfig=XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
	if(NULL == GicConfig)
		return XST_FAILURE;
		
	status=XScuGic_CfgInitialize(GicPtr,GicConfig,GicConfig->CpuBaseAddress);
	if (status != XST_SUCCESS)
			return XST_FAILURE;

	//Hardware initialization
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
				     (Xil_ExceptionHandler)XScuGic_InterruptHandler,
				     GicPtr);
	//Set channel 0 priority
	XScuGic_SetPriorityTriggerType(GicPtr,DMA0_INT_ID,160,1);
	//Connection interrupt handling function
	status = XScuGic_Connect(GicPtr,DMA0_INT_ID,(Xil_InterruptHandler)XDmaPs_DoneISR_0,(void *)DmaInstance);

		if (status != XST_SUCCESS)
				return XST_FAILURE;

		XScuGic_Enable(GicPtr, DMA0_INT_ID);

		Xil_ExceptionEnable();

		return XST_SUCCESS;
}

main.c function

#include "xparameters.h"
#include "uart.h"
#include "xil_printf.h"
#include "stdio.h"
#include "xil_types.h"
#include "xil_assert.h"
#include "xil_io.h"
#include "xil_exception.h"
#include "xil_cache.h"
#include "ps_dma.h"
#include "sleep.h"
#include <stdio.h>


u8 RecvBuffer[BUFFER_SZE];	//Receive data cache

u8 SendBuffer[BUFFER_SZE];	//Send data cache

u32 rec_data = 1 ;

/*It should be noted here that only one interrupt control driver instance can be enabled, because there is only one interrupt control. If two cards are instantiated, they will be interrupted*/
XScuGic Intc;              //Interrupt controller driver instance
/*********************************************************************************/

XUartPs Uart_Ps;           //Serial driver instance

XDmaPs DmaInstance;

static int Src[DMA_LENGTH];
static u8 Dst[DMA_LENGTH];

volatile int Checked[XDMAPS_CHANNELS_PER_DEV];

u8 uart_send[512];
u8 recv_total_byte;

XDmaPs DmaXDmaps;
XDmaPs_Cmd DmaCmd;

//main function
int main(void)
{
    int status=XST_SUCCESS;
    int Index;
    recv_total_byte=0;

    status= Uart_Init(&Uart_Ps);

    print("RUN1\r\n");

    status=Uart_Intr_Init(&Intc,&Uart_Ps);//Initialize serial port interrupt

    status=PsDMA_Init(&DmaXDmaps,&DmaCmd,&rec_data,&Dst);

    if(status!= XST_SUCCESS){
        	xil_printf("Error: XDMaPs_Example_W_Intr failed\r\n");
        	return XST_FAILURE;
        }

   status=PsDMA_Intr_Init(&DmaXDmaps,&Intc);//Initialize DMA interrupt

    for (Index = 0; Index < DMA_LENGTH; Index++)
    				Src[Index] = DMA_LENGTH-Index;


   for (Index = 0; Index < DMA_LENGTH; Index++)
       				Dst[Index] = 0;

   print("RUN\r\n");

    while (1){

    	for (Index = 0; Index < DMA_LENGTH; Index++) {
    	    	  printf("Src[%d],Dst[%d]:%d\r\n",Src[Index],Index,Dst[Index]);
    	    }

    	printf("rec_data:%d\r\n",(int)rec_data);

    	XDmaPs_Start(&DmaXDmaps,0,&DmaCmd,0);

    	printf("Is Life:%d\r\n",XDmaPs_IsActive(&DmaXDmaps,0));

    	rec_data++;
    	sleep(2);	//Delay 2s
    };
    return status;
}

When initializing an interrupt, remember to use the same interrupt controller driver instance, otherwise the card will be interrupted!

The serial port is added in the loopback process of LwIp

Import LwIp import history open platform_zynq.c file, in which TimerInstance is the interrupt controller driver instance of LwIp.

Therefore, you only need to fill in the modified instance during serial port interrupt initialization. There is no problem defining a new instance.

In the echo.c file, the red box part is the code to return the received data. Where p - > payload is the accepted data, and p-len represents the data length. You can use the memcpy function to copy the accepted data into an array to process the data. You can also add printf ("% s", P - > payload) here to send the received data to the serial port.

Posted by irbrian on Sat, 04 Dec 2021 20:36:07 -0800