DMA 简介#
DMA 是现代计算机的特色之一,是硬件实现存储器与存储器、存储器与 I/O 设备之间直接进行高速数据传输的内存技术,它允许不同速率的设备进行沟通,而不需要依靠 CPU 的中断负载。
如果不使用 DMA,那么 CPU 需要从数据源把每一个片段的数据复制到寄存器中,这个过程会一直占用 CPU 的资源。当使用 DMA 时,CPU 向 DMA 发送一个存储传输请求,当 DMA 控制器收到请求时就会将数据从源地址搬运到目的地址,搬运过程中不消化 CPU 的资源,当传输完成后 DMA 控制器以中断的方式通知 CPU。
为了发起传输事务,DMA 控制器必须有:
- 源地址
- 目的地址
- 传输长度
DMA 存储传输的过程如下: - 处理器向 DMA 控制器发送一条 DMA 命令
- DMA 控制器把数据从外设传输到存储器或从存储器搬运到存储器,而让 CPU 腾出手来做其它操作
- 数据传输完成后,DMA 控制器向 CPU 发出一个中断,来通知处理器 DMA 传输完成
ZYNQ 提供两种 DMA,一种是集成在 PS 中的硬核 DMA,一种是 PL 中的软核 AXI DMA IP。
各种接口方式的比较如下:
PL 的 DMA 和 AXI_HP 接口的传输适用于大量数据的高性能传输,带宽高,传输方式的拓扑图如下:
AXI DMA IP#
AXI Direct Memory Access(AXI DMA)IP 核在 AXI 4 内存映射和 AXI 4-Stream IP 接口之间提供高带宽直接储存访问。
AXI DMA 用到了三种总线。AXI 4-Lite 用于对寄存器进行配置,AXI 4 Memory Map 总线用于读写 DDR 中的数据,AXI 4 Stream 总线用于 AXI DMA 对外设数据的读写,其中 AXI 4 Stream Master(MM 2 S,Memory Map to Stream)接口用于向 PL 写入数据,AXI 4-Stream Slave (S 2 MM,Stream to Memory Map) 接口用于从 PL 读取数据。
AXI DMA 提供 3 种模式,分别是 Direct Register 模式、Scatter/Gather 模式和 Cyclic DMA(循环 DMA)模式,常用的是 Direct Register 模式。
Direct Register DMA 模式即 Simple DMA 模式。Direct Register 模式提供了一种配置,用于在 MM 2 S 和 S 2 MM 通道上执行简单的 DMA 传输。Simple DMA(简单 DMA)允许应用程序在 DMA 和 Device 之间定义单个事务。它有两个通道:一个从 DMA 到 Device,另一个从 Device 到 DMA。这里有个地方需要大家注意下,在编写 Simple DMA(简单 DMA)代码时必须设置缓冲区地址和长度字段以启动相应通道中的传输。(其中 PL 对应 Device)
Vivado 中的 IP 配置#
Enable Scatter Gather Engine
选中此选项可启用 Scatter Gather 模式操作,并在 AXI DMA 中包含 Scatter Gather Engine。取消选中此选项可启用 Direct Register 模式操作,但不包括 AXI DMA 中的 Scatter Gather Engine。禁用 Scatter Gather Engine 会使 Scatter/Gather Engine 的所有输出端口都绑定为零,并且所有输入端口都将保持打开状态。此处我们取消勾选 Enable Scatter Gather Engine
Enable Micro DMA
使能后会生成高度优化的 DMA,资源消耗较少,用于极少量数据的传输
Width of Buffer Length Register
该选项配置了 AXI DMA 单次最大能搬运多少个字节,字节数 $=2^{Width}$
Address Width (32 - 64)
指定地址空间的宽度,可以是 32 到 64 之间的任意值
Enable Read Channel
开启 AXI DMA 的读通道 MM 2 S,相关配置如下:
- Number of Channels:指定通道数。保持默认值 1
- Memory Map Data Width:AXI MM 2 S 存储映射读取数据总线的数据位宽。有效值为 32、64、128、256、512 和 1024。此处保持默认值 32
- Stream Data Width:AXI MM 2 S AXI 4-Stream 数据总线的数据位宽。该值必须小于或等于 Memory Map Data Width。有效值为 8、16、32、64、128、512 和 1024。此处保持默认值 32
- Max Burst Size:突发分区粒度设置。此设置指定 MM 2 S 的 AXI 4-Memory Map 侧的突发周期的最大值。有效值为 2、4、8、16、32、64、128 和 256
- Allow Unaligned Transfers:启用或禁用 MM 2 S 数据重新排列引擎(Data Realignment Engine,DRE)。选中时,DRE 被使能并允许在 MM 2 S 存储映射数据路径上数据重新对齐到 8 位的字节水平。对于 MM 2 S 通道,则从内存中读取数据。如果 DRE 被使能,则数据读取可以从任何缓冲区地址字节偏移开始,并且读取数据被对齐,使得第一个字节读取是 AXI 4-Stream 上的第一个有效字节输出
Enable Write Channel
开启 AXI DMA 的写通道 S 2 MM,相关配置同上
Vitis 工程解析#
头文件引用和宏定义#
/***************************** Include Files *********************************/
#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xil_util.h"
#ifdef XPAR_UARTNS550_0_BASEADDR
#include "xuartns550_l.h" /* to use uartns550 */
#endif
#ifdef XPAR_INTC_0_DEVICE_ID
#include "xintc.h"
#else
#include "xscugic.h"
#endif
/************************** Constant Definitions *****************************/
/*
* Device hardware build related constants.
*/
#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
#ifdef XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
#define DDR_BASE_ADDR XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
#elif defined(XPAR_MIG7SERIES_0_BASEADDR)
#define DDR_BASE_ADDR XPAR_MIG7SERIES_0_BASEADDR
#elif defined(XPAR_MIG_0_C0_DDR4_MEMORY_MAP_BASEADDR)
#define DDR_BASE_ADDR XPAR_MIG_0_C0_DDR4_MEMORY_MAP_BASEADDR
#elif defined(XPAR_PSU_DDR_0_S_AXI_BASEADDR)
#define DDR_BASE_ADDR XPAR_PSU_DDR_0_S_AXI_BASEADDR
#endif
#define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR // 0x00100000
#ifndef DDR_BASE_ADDR
#warning CHECK FOR THE VALID DDR ADDRESS IN XPARAMETERS.H, \
DEFAULT SET TO 0x01000000
#define MEM_BASE_ADDR 0x01000000
#else
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x01000000) // 0x01100000
#endif
#ifdef XPAR_INTC_0_DEVICE_ID
#define RX_INTR_ID XPAR_INTC_0_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define TX_INTR_ID XPAR_INTC_0_AXIDMA_0_MM2S_INTROUT_VEC_ID
#else
#define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
#endif
#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) // 0x01200000
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) // 0x01400000
#define RX_BUFFER_HIGH (MEM_BASE_ADDR + 0x004FFFFF) // 0x015FFFFF
#ifdef XPAR_INTC_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_INTC_0_DEVICE_ID
#else
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#endif
#ifdef XPAR_INTC_0_DEVICE_ID
#define INTC XIntc
#define INTC_HANDLER XIntc_InterruptHandler
#else
#define INTC XScuGic
#define INTC_HANDLER XScuGic_InterruptHandler
#endif
/* Timeout loop counter for reset
*/
#define RESET_TIMEOUT_COUNTER 10000
#define TEST_START_VALUE 0x1
/*
* Buffer and Buffer Descriptor related constant definition
*/
#define MAX_PKT_LEN 0x200
#define NUMBER_OF_TRANSFERS 10
#define POLL_TIMEOUT_COUNTER 1000000U
#define NUMBER_OF_EVENTS 1
#define DDR_BASE_ADDR XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
重新定义了 DDR 3 的基址.。数据读写的基址为 #define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x01000000) // 0x01100000
AXI DMA 读取数据的起始地址为 #define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) // 0x01200000
,写入地址为 0x01400000
定义函数和声明相关的 instance 和 flag 变量#
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
#ifndef DEBUG
extern void xil_printf(const char *format, ...);
#endif
#ifdef XPAR_UARTNS550_0_BASEADDR
static void Uart550_Setup(void);
#endif
static int CheckData(int Length, u8 StartValue);
static void TxIntrHandler(void *Callback);
static void RxIntrHandler(void *Callback);
static int SetupIntrSystem(INTC *IntcInstancePtr,
XAxiDma *AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);
static void DisableIntrSystem(INTC *IntcInstancePtr,
u16 TxIntrId, u16 RxIntrId);
/************************** Variable Definitions *****************************/
/*
* Device instance definitions
*/
static XAxiDma AxiDma; /* Instance of the XAxiDma */
static INTC Intc; /* Instance of the Interrupt Controller */
/*
* Flags interrupt handlers use to notify the application context the events.
*/
volatile u32 TxDone;
volatile u32 RxDone;
volatile u32 Error;
Main 函数#
/*****************************************************************************/
/**
*
* Main function
*
* This function is the main entry of the interrupt test. It does the following:
* Set up the output terminal if UART16550 is in the hardware build
* Initialize the DMA engine
* Set up Tx and Rx channels
* Set up the interrupt system for the Tx and Rx interrupts
* Submit a transfer
* Wait for the transfer to finish
* Check transfer status
* Disable Tx and Rx interrupts
* Print test status and exit
*
* @param None
*
* @return
* - XST_SUCCESS if example finishes successfully
* - XST_FAILURE if example fails.
*
* @note None.
*
******************************************************************************/
int main(void)
{
int Status;
XAxiDma_Config *Config;
int Tries = NUMBER_OF_TRANSFERS;
int Index;
u8 *TxBufferPtr;
u8 *RxBufferPtr;
u8 Value;
TxBufferPtr = (u8 *)TX_BUFFER_BASE;
RxBufferPtr = (u8 *)RX_BUFFER_BASE;
/* Initial setup for Uart16550 */
#ifdef XPAR_UARTNS550_0_BASEADDR
Uart550_Setup();
#endif
xil_printf("\r\n--- Entering main() --- \r\n");
Config = XAxiDma_LookupConfig(DMA_DEV_ID);
if (!Config)
{
xil_printf("No config found for %d\r\n", DMA_DEV_ID);
return XST_FAILURE;
}
else
{
xil_printf("Config found for %d\r\n", DMA_DEV_ID);
}
/* Initialize DMA engine */
Status = XAxiDma_CfgInitialize(&AxiDma, Config);
if (Status != XST_SUCCESS)
{
xil_printf("Initialization failed %d\r\n", Status);
return XST_FAILURE;
}
else
{
xil_printf("DMA Initialization success %d\r\n", Status);
}
if (XAxiDma_HasSg(&AxiDma))
{
xil_printf("Device configured as SG mode \r\n");
return XST_FAILURE;
}
else
{
xil_printf("Device configured as simple mode \r\n");
}
/* Set up Interrupt system */
Status = SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
if (Status != XST_SUCCESS)
{
xil_printf("Failed intr setup\r\n");
return XST_FAILURE;
}
/* Disable all interrupts before setup */
XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
XAXIDMA_DMA_TO_DEVICE);
XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
XAXIDMA_DEVICE_TO_DMA);
/* Enable all interrupts */
XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
XAXIDMA_DMA_TO_DEVICE);
XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
XAXIDMA_DEVICE_TO_DMA);
/* Initialize flags before start transfer test */
TxDone = 0;
RxDone = 0;
Error = 0;
Value = TEST_START_VALUE;
for (Index = 0; Index < MAX_PKT_LEN; Index++)
{
TxBufferPtr[Index] = Value;
Value = (Value + 1) & 0xFF;
}
/* Flush the buffers before the DMA transfer, in case the Data Cache
* is enabled
*/
Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);
/* Send a packet */
for (int i = 0; i < Tries; i++)
{
Status = XAxiDma_SimpleTransfer(&AxiDma, (UINTPTR)RxBufferPtr,
MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
if (Status != XST_SUCCESS)
{
return XST_FAILURE;
}
Status = XAxiDma_SimpleTransfer(&AxiDma, (UINTPTR)TxBufferPtr,
MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
if (Status != XST_SUCCESS)
{
return XST_FAILURE;
}
Status = Xil_WaitForEventSet(POLL_TIMEOUT_COUNTER, NUMBER_OF_EVENTS, &Error);
if (Status == XST_SUCCESS)
{
if (!TxDone)
{
xil_printf("Transmit error %d\r\n", Status);
goto Done;
}
else if (Status == XST_SUCCESS && !RxDone)
{
xil_printf("Receive error %d\r\n", Status);
goto Done;
}
}
/*
* Wait for TX done or timeout
*/
Status = Xil_WaitForEventSet(POLL_TIMEOUT_COUNTER, NUMBER_OF_EVENTS, &TxDone);
if (Status != XST_SUCCESS)
{
xil_printf("Transmit failed %d\r\n", Status);
goto Done;
}
/*
* Wait for RX done or timeout
*/
Status = Xil_WaitForEventSet(POLL_TIMEOUT_COUNTER, NUMBER_OF_EVENTS, &RxDone);
if (Status != XST_SUCCESS)
{
xil_printf("Receive failed %d\r\n", Status);
goto Done;
}
/*
* Test finished, check data
*/
Status = CheckData(MAX_PKT_LEN, 0x1);
if (Status != XST_SUCCESS)
{
xil_printf("Data check failed\r\n");
goto Done;
}
xil_printf("Transfer %d done\r\n", i + 1);
}
xil_printf("Successfully ran AXI DMA interrupt Example\r\n");
/* Disable TX and RX Ring interrupts and return success */
DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);
Done:
xil_printf("--- Exiting main() --- \r\n");
if (Status != XST_SUCCESS)
{
return XST_FAILURE;
}
return XST_SUCCESS;
}
Tries
为测试回环的次数
函数的实现#
*****************************************************************************/
/*
*
* This function checks data buffer after the DMA transfer is finished.
*
* We use the static tx/rx buffers.
*
* @param Length is the length to check
* @param StartValue is the starting value of the first byte
*
* @return
* - XST_SUCCESS if validation is successful
* - XST_FAILURE if validation is failure.
*
* @note None.
*
******************************************************************************/
static int CheckData(int Length, u8 StartValue)
{
u8 *RxPacket;
int Index = 0;
u8 Value;
RxPacket = (u8 *)RX_BUFFER_BASE;
Value = StartValue;
/* Invalidate the DestBuffer before receiving the data, in case the
* Data Cache is enabled
*/
Xil_DCacheInvalidateRange((UINTPTR)RxPacket, Length);
for (Index = 0; Index < Length; Index++)
{
if (RxPacket[Index] != Value)
{
xil_printf("Data error %d: %x/%x\r\n",
Index, RxPacket[Index], Value);
return XST_FAILURE;
}
Value = (Value + 1) & 0xFF;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/*
*
* This is the DMA TX Interrupt handler function.
*
* It gets the interrupt status from the hardware, acknowledges it, and if any
* error happens, it resets the hardware. Otherwise, if a completion interrupt
* is present, then sets the TxDone.flag
*
* @param Callback is a pointer to TX channel of the DMA engine.
*
* @return None.
*
* @note None.
*
******************************************************************************/
static void TxIntrHandler(void *Callback)
{
u32 IrqStatus;
int TimeOut;
XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
/* Read pending interrupts */
IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);
/* Acknowledge pending interrupts */
XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);
/*
* If no interrupt is asserted, we do not do anything
*/
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK))
{
return;
}
/*
* If error interrupt is asserted, raise error flag, reset the
* hardware to recover from the error, and return with no further
* processing.
*/
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK))
{
Error = 1;
/*
* Reset should never fail for transmit channel
*/
XAxiDma_Reset(AxiDmaInst);
TimeOut = RESET_TIMEOUT_COUNTER;
while (TimeOut)
{
if (XAxiDma_ResetIsDone(AxiDmaInst))
{
break;
}
TimeOut -= 1;
}
return;
}
/*
* If Completion interrupt is asserted, then set the TxDone flag
*/
if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK))
{
TxDone = 1;
}
}
/*****************************************************************************/
/*
*
* This is the DMA RX interrupt handler function
*
* It gets the interrupt status from the hardware, acknowledges it, and if any
* error happens, it resets the hardware. Otherwise, if a completion interrupt
* is present, then it sets the RxDone flag.
*
* @param Callback is a pointer to RX channel of the DMA engine.
*
* @return None.
*
* @note None.
*
******************************************************************************/
static void RxIntrHandler(void *Callback)
{
u32 IrqStatus;
int TimeOut;
XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
/* Read pending interrupts */
IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);
/* Acknowledge pending interrupts */
XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);
/*
* If no interrupt is asserted, we do not do anything
*/
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK))
{
return;
}
/*
* If error interrupt is asserted, raise error flag, reset the
* hardware to recover from the error, and return with no further
* processing.
*/
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK))
{
Error = 1;
/* Reset could fail and hang
* NEED a way to handle this or do not call it??
*/
XAxiDma_Reset(AxiDmaInst);
TimeOut = RESET_TIMEOUT_COUNTER;
while (TimeOut)
{
if (XAxiDma_ResetIsDone(AxiDmaInst))
{
break;
}
TimeOut -= 1;
}
return;
}
/*
* If completion interrupt is asserted, then set RxDone flag
*/
if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK))
{
RxDone = 1;
}
}
/*****************************************************************************/
/*
*
* This function setups the interrupt system so interrupts can occur for the
* DMA, it assumes INTC component exists in the hardware system.
*
* @param IntcInstancePtr is a pointer to the instance of the INTC.
* @param AxiDmaPtr is a pointer to the instance of the DMA engine
* @param TxIntrId is the TX channel Interrupt ID.
* @param RxIntrId is the RX channel Interrupt ID.
*
* @return
* - XST_SUCCESS if successful,
* - XST_FAILURE.if not successful
*
* @note None.
*
******************************************************************************/
static int SetupIntrSystem(INTC *IntcInstancePtr,
XAxiDma *AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
int Status;
#ifdef XPAR_INTC_0_DEVICE_ID
/* Initialize the interrupt controller and connect the ISRs */
Status = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
if (Status != XST_SUCCESS)
{
xil_printf("Failed init intc\r\n");
return XST_FAILURE;
}
Status = XIntc_Connect(IntcInstancePtr, TxIntrId,
(XInterruptHandler)TxIntrHandler, AxiDmaPtr);
if (Status != XST_SUCCESS)
{
xil_printf("Failed tx connect intc\r\n");
return XST_FAILURE;
}
Status = XIntc_Connect(IntcInstancePtr, RxIntrId,
(XInterruptHandler)RxIntrHandler, AxiDmaPtr);
if (Status != XST_SUCCESS)
{
xil_printf("Failed rx connect intc\r\n");
return XST_FAILURE;
}
/* Start the interrupt controller */
Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
if (Status != XST_SUCCESS)
{
xil_printf("Failed to start intc\r\n");
return XST_FAILURE;
}
XIntc_Enable(IntcInstancePtr, TxIntrId);
XIntc_Enable(IntcInstancePtr, RxIntrId);
#else
XScuGic_Config *IntcConfig;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig)
{
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS)
{
return XST_FAILURE;
}
//设置中断优先级和触发类型
XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
/*
* Connect the device driver handler that will be called when an
* interrupt for the device occurs, the handler defined above performs
* the specific interrupt processing for the device.
*/
//设置中断处理函数
Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
(Xil_InterruptHandler)TxIntrHandler,
AxiDmaPtr);
if (Status != XST_SUCCESS)
{
return Status;
}
Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
(Xil_InterruptHandler)RxIntrHandler,
AxiDmaPtr);
if (Status != XST_SUCCESS)
{
return Status;
}
XScuGic_Enable(IntcInstancePtr, TxIntrId);
XScuGic_Enable(IntcInstancePtr, RxIntrId);
#endif
/* Enable interrupts from the hardware */
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)INTC_HANDLER,
(void *)IntcInstancePtr);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function disables the interrupts for DMA engine.
*
* @param IntcInstancePtr is the pointer to the INTC component instance
* @param TxIntrId is interrupt ID associated w/ DMA TX channel
* @param RxIntrId is interrupt ID associated w/ DMA RX channel
*
* @return None.
*
* @note None.
*
******************************************************************************/
static void DisableIntrSystem(INTC *IntcInstancePtr,
u16 TxIntrId, u16 RxIntrId)
{
#ifdef XPAR_INTC_0_DEVICE_ID
/* Disconnect the interrupts for the DMA TX and RX channels */
XIntc_Disconnect(IntcInstancePtr, TxIntrId);
XIntc_Disconnect(IntcInstancePtr, RxIntrId);
#else
XScuGic_Disconnect(IntcInstancePtr, TxIntrId);
XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
#endif
}