ZYNQ裸板:AXIDMA篇(简单模式)

简介: DMA(Direct Memory Access,直接存储器访问)是计算机科学中的一种内存访问技术。它允许某些计算机内部的硬件子系统可以独立地直接读写系统内存,而不需中央处理器( CPU)介入处理。 DMA 是一种快速的数据传送方式, 通常用来传送数据量较多的数据块

前言

 DMA(Direct Memory Access,直接存储器访问)是计算机科学中的一种内存访问技术。它允许某些计算机内部的硬件子系统可以独立地直接读写系统内存,而不需中央处理器( CPU)介入处理。 DMA 是一种快速的数据传送方式, 通常用来传送数据量较多的数据块

 使用 DMA时, CPU 向 DMA 控制器发出一个存储传输请求, 这样当 DMA 控制器在传输的时候, CPU 执行其它操作,传输操作完成时 DMA 以中断的方式通知 CPU。

 为了发起传输事务, DMA 控制器必须得到以下数据:

• 源地址 — 数据被读出的地址

• 目的地址 — 数据被写入的地址

• 传输长度 — 应被传输的字节数

20210209141318133.png

DMA 存储传输的过程如下:

为了配置用 DMA 传输数据到存储器,处理器发出一条 DMA 命令

DMA 控制器把数据从外设传输到存储器或从存储器到存储器,而让 CPU 腾出手来做其它操作。

数据传输完成后,向 CPU 发出一个中断来通知它 DMA 传输可以关闭了。ZYNQ 提供了两种 DMA,一种是集成在 PS 中的硬核 DMA,另一种是 PL 中使用的软核 AXI DMA IP。

在 ARM CPU 设计的过程中,已经考虑到了大量数据搬移的情况,因此在 CPU 中自带了一个 DMA 控制器 DAMC,这个 DAMC 驻留在 PS 内,而且必须通过驻留在内存中的 DMA 指令编程,这些程序往往由CPU 准备,因此需要部分的 CPU 参与。 DMAC 支持高达 8 个通道,所以多个 DMA 结构的核可以挂在单个DMAC 上。 DAMC 与 PL 的连接是通过 AXI_GP 接口,这个接口最高支持到 32 位宽度,这也限制了这种模式下的传输速率,理论最高速率为 600MB/s。这种模式不占用 PL 资源,但需要对 DMA 指令编程,会增加软件的复杂性。

为了获取更高的传输速率,可以以空间换时间,在 PL 中添加 AXI DMA IP 核,并利用 AXI_HP 接口完成高速的数据传输。原子将各个接口方式的比较做了表格,很是详细

20210209141446164.png

 可见通过 PL 的 DMA 和 AXI_HP 接口的传输适用于大块数据的高性能传输,带宽高。

 下面我们简单的介绍下 PL 的 DMA,即 AXI DMA IP 核。

 AXI Direct Memory Access( AXI DMA) IP 内核在 AXI4 内存映射和 AXI4-Stream IP 接口之间提供高带宽直接储存访问。其可选的 scatter gather 功能还可以从基于处理器的系统中的中央处理单元( CPU)卸载数据移动任务。初始化、 状态和管理寄存器通过 AXI4-Lite 从接口访问。核心的功能组成如下图所示:

20210209141530325.png

 AXI DMA 用到了三种总线, AXI4-Lite 用于对寄存器进行配置, AXI4 Memory Map 用于与内存交互,又分为 AXI4 Memory Map Read 和 AXI4 Memory Map Write 两个接口,一个是读一个是写。 AXI4 Stream 接口用于对外设的读写,其中 AXI4 Stream Master( MM2S, Memory Map to Stream)用于对外设写, AXI4-Stream Slave(S2MM, Stream to Memory Map)用于对外设读。总之,在以后的使用中需要知道 AXI_MM2S 和AXI_S2MM 是存储器端映射的 AXI4 总线,提供对存储器( DDR3)的访问。 AXIS_MM2S 和 AXIS_S2MM是 AXI4-streaming 总线,可以发送和接收连续的数据流,无需地址。

 AXI DMA 提供 3 种模式,分别是 Direct Register 模式、 Scatter/Gather 模式和 Cyclic DMA 模式,这里我们简单的介绍下常用的 Direct Register 模式和 Scatter/Gather 模式。

 Direct Register DMA 模式也就是 Simple DMA。 Direct Register 模式提供了一种配置,用于在 MM2S 和S2MM 通道上执行简单的 DMA 传输,这需要更少的 FPGA 资源。 Simple DMA 允许应用程序在 DMA 和Device 之间定义单个事务。它有两个通道:一个从 DMA 到 Device,另一个从 Device 到 DMA。应用程序必须设置缓冲区地址和长度字段以启动相应通道中的传输。

 Scatter/Gather DMA 模式允许在单个 DMA 事务中将数据传输到多个存储区域或从多个存储区域传输数据。它相当于将多个 Simple DMA 请求链接在一起。 SGDMA 允许应用程序在内存中定义事务列表,硬件将在应用程序没有进一步干预的情况下处理这些事务。在此期间,应用程序可以继续添加更多工作以保持硬件工作。用户可以通过轮询或中断来检查事务是否完成。 SGDMA 处理整个数据包( 被定义为表示消息的一系列数据字节)并允许将数据包分解为一个或多个事务。例如,采用以太网 IP 数据包,该数据包由 14 字节的报头后跟 1 个或多个字节的有效负载组成。使用 SGDMA,应用程序可以将 BD( Buffer Descriptor, 用于描述事务的对象) 指向报头,将另一个 BD 指向有效负载,然后将它们作为单个消息传输。这种策略可以使TCP / IP 堆栈更有效,它允许将数据包标头和数据保存在不同的内存区域,而不是将数据包组装成连续的内存块。

 在此次的工程中暂时用不到SG模式,如果用sata这些的话估计得用到了,正常情况下简单模式还是够用的

下面做一个简单的AXIDMA初始化和收发demo测试

头文件如下

#ifndef SRC_XDMA_DRIVER_H_
#define SRC_XDMA_DRIVER_H_
#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.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
#include "xgpiops.h"
/************************** 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 XPAR_MIG7SERIES_0_BASEADDR
#define DDR_BASE_ADDR XPAR_MIG7SERIES_0_BASEADDR
#elif XPAR_MIG_0_BASEADDR
#define DDR_BASE_ADDR XPAR_MIG_0_BASEADDR
#elif XPAR_PSU_DDR_0_S_AXI_BASEADDR
#define DDR_BASE_ADDR XPAR_PSU_DDR_0_S_AXI_BASEADDR
#endif
#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 + 0x1000000)
#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)
#define TX_BUFFER_BASE1  (MEM_BASE_ADDR + 0x00101000)
#define TX_BUFFER_BASE2  (MEM_BASE_ADDR + 0x00102000)
#define TX_BUFFER_BASE3  (MEM_BASE_ADDR + 0x00103000)
#define RX_BUFFER_BASE  (MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_BASE1  (MEM_BASE_ADDR + 0x00301000)
#define RX_BUFFER_BASE2  (MEM_BASE_ADDR + 0x00302000)
#define RX_BUFFER_BASE3  (MEM_BASE_ADDR + 0x00303000)
#define RX_BUFFER_HIGH  (MEM_BASE_ADDR + 0x004FFFFF)
#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
extern XAxiDma AxiDma;  /* Instance of the XAxiDma */
extern XAxiDma AxiDma1;  /* Instance of the XAxiDma */
extern XAxiDma AxiDma2;  /* Instance of the XAxiDma */
extern XAxiDma AxiDma3;  /* Instance of the XAxiDma */
/* Timeout loop counter for reset
 */
#define RESET_TIMEOUT_COUNTER 10000
#define TEST_START_VALUE  0xC
/*
 * Buffer and Buffer Descriptor related constant definition
 */
#define MAX_PKT_LEN  0x100
#define NUMBER_OF_TRANSFERS 10
static XGpioPs Gpio; /* The Instance of the GPIO Driver */
/* The interrupt coalescing threshold and delay timer threshold
 * Valid range is 1 to 255
 *
 * We set the coalescing threshold to be the total number of packets.
 * The receive side will only get one completion interrupt for this example.
 */
/************* Type Definitions ******************/
/***** Macros (Inline Functions) Definitions **********/
/************* Function Prototypes **************/
#ifndef DEBUG
extern void xil_printf(const char *format, ...);
#endif
#ifdef XPAR_UARTNS550_0_BASEADDR
 void Uart550_Setup(void);
#endif
static int CheckData(int Length, u8 StartValue);
//static void TxIntrHandler(void *Callback);
//static void RxIntrHandler(void *Callback);
int dma_init(void);
int dma_8_10b_send(XAxiDma* AxiDmaIns,u8 *wBuffer , unsigned int length);
unsigned int dma_8_10b_recv(XAxiDma* AxiDmaIns,u8 *rBuffer);
static int SetupIntrSystem(INTC * IntcInstancePtr,
      XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);
static void DisableIntrSystem(INTC * IntcInstancePtr,
      u16 TxIntrId, u16 RxIntrId);
 int SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,
    u16 GpioIntrId);
static void TxIntrHandler(AxiDma);
static void RxIntrHandler(AxiDma);
/************** Variable Definitions ****************/
/*
 * Device instance definitions
 */
static INTC Intc; /* Instance of the Interrupt Controller */
/*
 * Flags interrupt handlers use to notify the application context the events.
 */
volatile int TxDone;
volatile int DMA0RxDone;
volatile int DMA1RxDone;
volatile int DMA2RxDone;
volatile int DMA3RxDone;
volatile int Error;
#endif /* SRC_XDMA_DRIVER_H_ */

驱动文件如下

#include "xdma_driver.h"
XAxiDma AxiDma;  /* Instance of the XAxiDma */
XAxiDma AxiDma1;  /* Instance of the XAxiDma */
XAxiDma AxiDma2;  /* Instance of the XAxiDma */
XAxiDma AxiDma3;  /* Instance of the XAxiDma */
#ifdef XPAR_UARTNS550_0_BASEADDR
/*****************************************************************************/
/*
*
* Uart16550 setup routine, need to set baudrate to 9600 and data bits to 8
*
* @param  None
*
* @return None
*
* @note  None.
*
******************************************************************************/
extern void Uart550_Setup(void)
{
  XUartNs550_SetBaud(XPAR_UARTNS550_0_BASEADDR,
    XPAR_XUARTNS550_CLOCK_HZ, 9600);
  XUartNs550_SetLineControlReg(XPAR_UARTNS550_0_BASEADDR,
    XUN_LCR_8_DATA_BITS);
}
#endif
/*****************************************************************************/
/*
*
* 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.
*
******************************************************************************/
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
  */
#ifndef __aarch64__
  Xil_DCacheInvalidateRange((UINTPTR)RxPacket, Length);
#endif
  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)
static void TxIntrHandler(XAxiDma *AxiDma)
{
  u32 IrqStatus;
  int TimeOut;
  //XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
  /* Read pending interrupts */
  IrqStatus = XAxiDma_IntrGetIrq(AxiDma, XAXIDMA_DMA_TO_DEVICE);
  /* Acknowledge pending interrupts */
  XAxiDma_IntrAckIrq(AxiDma, 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(AxiDma);
  TimeOut = RESET_TIMEOUT_COUNTER;
  while (TimeOut) {
    if (XAxiDma_ResetIsDone(AxiDma)) {
    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)
static void RxIntrHandler(XAxiDma *AxiDmaIns )
{
  u32 IrqStatus;
  int TimeOut;
  //XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
  /* Read pending interrupts */
  IrqStatus = XAxiDma_IntrGetIrq(AxiDmaIns, XAXIDMA_DEVICE_TO_DMA);
  /* Acknowledge pending interrupts */
  XAxiDma_IntrAckIrq(AxiDmaIns, 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(AxiDmaIns);
  TimeOut = RESET_TIMEOUT_COUNTER;
  while (TimeOut) {
    if(XAxiDma_ResetIsDone(AxiDmaIns)) {
    break;
    }
    TimeOut -= 1;
  }
  return;
  }
  /*
  * If completion interrupt is asserted, then set RxDone flag
  */
  if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK))
  {
  if(AxiDmaIns == &AxiDma)
  {
      DMA0RxDone = 1;
  }
  else if(AxiDmaIns == &AxiDma1)
  {
    DMA1RxDone = 1;
  }
  else if(AxiDmaIns == &AxiDma2)
  {
    DMA2RxDone = 1;
  }
  else if(AxiDmaIns == &AxiDma3)
  {
    DMA3RxDone = 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 succesful
*
* @note  None.
*
******************************************************************************/
static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
  XGpioPs *Gpio = (XGpioPs *)CallBackRef;
  u32 DataRead = 0;;
  /* Push the switch button */
  //DataRead = XGpioPs_ReadPin(Gpio, Input_Pin);
  DataRead = XGpioPs_Read(Gpio, 2);
  xil_printf("0x%x IntrHandler\r\n",DataRead);
  if ((DataRead & 0x1) == 0x01)
  {
  TxIntrHandler(&AxiDma);
  }
  if((DataRead & 0x2) == 0x02)
  {
  RxIntrHandler(&AxiDma);
  }
  if ((DataRead & 0x4) == 0x04)
  {
  TxIntrHandler(&AxiDma1);
//  xil_printf("data 4t\r\n");
  }
  if((DataRead & 0x8) == 0x08)
  {
  RxIntrHandler(&AxiDma1);
//  xil_printf("data 4r\r\n");
  }
  if ((DataRead & 0x10) == 0x010)
  {
  TxIntrHandler(&AxiDma2);
  }
  if((DataRead & 0x20) == 0x20)
  {
  RxIntrHandler(&AxiDma2);
  }
  if ((DataRead & 0x40) == 0x40)
  {
  TxIntrHandler(&AxiDma3);
  }
  if((DataRead & 0x80) == 0x080)
  {
  RxIntrHandler(&AxiDma3);
  }
  else
  {
  }
}
/*****************************************************************************/
/**
*
* This function sets up the interrupt system for the example. It enables falling
* edge interrupts for all the pins of bank 0 in the GPIO device.
*
* @param  GicInstancePtr is a pointer to the XScuGic driver Instance.
* @param  GpioInstancePtr contains a pointer to the instance of the GPIO
*  component which is going to be connected to the interrupt
*  controller.
* @param  GpioIntrId is the interrupt Id and is typically
*  XPAR_<GICPS>_<GPIOPS_instance>_VEC_ID value from
*  xparameters.h.
*
* @return XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note  None.
*
****************************************************************************/
 int SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,
    u16 GpioIntrId)
{
  xil_printf("SetupInterruptSystem\r\n");
  int Status;
  XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */
  Xil_ExceptionInit();
  /*
  * 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(GicInstancePtr, IntcConfig,
      IntcConfig->CpuBaseAddress);
  if (Status != XST_SUCCESS) {
  return XST_FAILURE;
  }
  /*
  * Connect the interrupt controller interrupt handler to the hardware
  * interrupt handling logic in the processor.
  */
  Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
    (Xil_ExceptionHandler)XScuGic_InterruptHandler,
    GicInstancePtr);
  /*
  * 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(GicInstancePtr, GpioIntrId,
    (Xil_ExceptionHandler)XGpioPs_IntrHandler,
    (void *)Gpio);
  if (Status != XST_SUCCESS) {
  return Status;
  }
  /* Enable falling edge interrupts for all the pins in bank 0. */
  XGpioPs_SetIntrType(Gpio, 2, 0x00, 0xFFFFFFFF, 0x00);
  /* Set the handler for gpio interrupts. */
  XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);
  /* Enable the GPIO interrupts of Bank 2. */
  XGpioPs_IntrEnable(Gpio, 2, 0xFF);
  /* Enable the interrupt for the GPIO device. */
  XScuGic_Enable(GicInstancePtr, GpioIntrId);
  /* Enable interrupts in the Processor. */
  Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
  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
}
int dma_init(void)
{
  int Status;
  XAxiDma_Config *Config;
  XAxiDma_Config *Config1;
  XAxiDma_Config *Config2;
  XAxiDma_Config *Config3;
  XGpioPs_Config *ConfigPtr;
  u8 *RxBufferPtr = (u8 *)RX_BUFFER_BASE;
  u8 *RxBufferPtr1 = (u8 *)RX_BUFFER_BASE1;
  u8 *RxBufferPtr2 = (u8 *)RX_BUFFER_BASE2;
  u8 *RxBufferPtr3 = (u8 *)RX_BUFFER_BASE3;
  memset(RxBufferPtr,0,MAX_PKT_LEN);
  memset(RxBufferPtr1,0,MAX_PKT_LEN);
  memset(RxBufferPtr2,0,MAX_PKT_LEN);
  memset(RxBufferPtr3,0,MAX_PKT_LEN);
  xil_printf("\r\n--- Entering dma init() --- \r\n");
  Config = XAxiDma_LookupConfig(0);
  if (!Config) {
  xil_printf("No config found for 0\r\n");
  return XST_FAILURE;
  }
  Config1 = XAxiDma_LookupConfig(1);
  if (!Config1) {
  xil_printf("No config found for 1\r\n");
  return XST_FAILURE;
  }
  Config2 = XAxiDma_LookupConfig(2);
  if (!Config2) {
  xil_printf("No config found for 2\r\n");
  return XST_FAILURE;
  }
  Config3 = XAxiDma_LookupConfig(3);
  if (!Config3) {
  xil_printf("No config found for 3\r\n");
  return XST_FAILURE;
  }
  /* Initialize DMA engine */
  Status = XAxiDma_CfgInitialize(&AxiDma, Config);
  if (Status != XST_SUCCESS) {
    xil_printf("Initialization 0 failed %d\r\n", Status);
    return XST_FAILURE;
  }
  Status = XAxiDma_CfgInitialize(&AxiDma1, Config1);
  if (Status != XST_SUCCESS) {
    xil_printf("Initialization 1 failed %d\r\n", Status);
    return XST_FAILURE;
  }
  Status = XAxiDma_CfgInitialize(&AxiDma2, Config2);
  if (Status != XST_SUCCESS) {
    xil_printf("Initialization 2 failed %d\r\n", Status);
    return XST_FAILURE;
  }
  Status = XAxiDma_CfgInitialize(&AxiDma3, Config3);
  if (Status != XST_SUCCESS) {
    xil_printf("Initialization 3 failed %d\r\n", Status);
    return XST_FAILURE;
  }
  /* Initialize the Gpio driver. */
  ConfigPtr = XGpioPs_LookupConfig(0);
  if (ConfigPtr == NULL) {
  return XST_FAILURE;
  }
  XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
  /* Set the direction for the specified pin to be input */
  XGpioPs_SetDirectionPin(&Gpio,54,0);
  XGpioPs_SetDirectionPin(&Gpio,55,0);
  XGpioPs_SetDirectionPin(&Gpio,56,0);
  XGpioPs_SetDirectionPin(&Gpio,57,0);
  XGpioPs_SetDirectionPin(&Gpio,58,0);
  XGpioPs_SetDirectionPin(&Gpio,59,0);
  XGpioPs_SetDirectionPin(&Gpio,60,0);
  XGpioPs_SetDirectionPin(&Gpio,61,0);
  /*
  * Setup the interrupts such that interrupt processing can occur. If
  * an error occurs then exit.
  */
  Status = SetupInterruptSystem(&Intc, &Gpio, XPAR_XGPIOPS_0_INTR);
  if (Status != XST_SUCCESS) {
  return XST_FAILURE;
  }
  XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
  XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
  XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
  XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrDisable(&AxiDma1, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
  XAxiDma_IntrDisable(&AxiDma1, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
  XAxiDma_IntrEnable(&AxiDma1, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
  XAxiDma_IntrEnable(&AxiDma1, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrDisable(&AxiDma2, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
  XAxiDma_IntrDisable(&AxiDma2, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
  XAxiDma_IntrEnable(&AxiDma2, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
  XAxiDma_IntrEnable(&AxiDma2, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrDisable(&AxiDma3, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
  XAxiDma_IntrDisable(&AxiDma3, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
  XAxiDma_IntrEnable(&AxiDma3, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
  XAxiDma_IntrEnable(&AxiDma3, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
  /**************************************************************************/
//  Status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR,0x58);
//  xil_printf(" RX length is 0x%x\r\n",Status);
//  Status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR,0x28);
//  xil_printf("TX length is 0x%x\r\n",Status);
  /**************************************************************************/
  Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr,
    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
  if (Status != XST_SUCCESS) {
  return XST_FAILURE;
  }
  Status = XAxiDma_SimpleTransfer(&AxiDma1,(UINTPTR) RxBufferPtr1,
    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
  if (Status != XST_SUCCESS) {
  return XST_FAILURE;
  }
  Status = XAxiDma_SimpleTransfer(&AxiDma2,(UINTPTR) RxBufferPtr2,
    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
  if (Status != XST_SUCCESS) {
  return XST_FAILURE;
  }
  Status = XAxiDma_SimpleTransfer(&AxiDma3,(UINTPTR) RxBufferPtr3,
    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
  if (Status != XST_SUCCESS) {
  return XST_FAILURE;
  }
  /* Initialize flags before start transfer test  */
  TxDone = 0;
  DMA0RxDone = 0;
  DMA1RxDone = 0;
  DMA2RxDone = 0;
  DMA3RxDone = 0;
  Error = 0;
}
int dma_8_10b_send(XAxiDma* AxiDmaIns,u8 *wBuffer , unsigned int length)
{
  int Status;
  u8 *TxBufferPtr;
  if((AxiDmaIns == NULL)||(wBuffer == NULL))
  {
  xil_printf("send Pointer err\r\n");
  return XST_FAILURE;
  }
  if (AxiDmaIns == &AxiDma)
  TxBufferPtr = (u8 *)TX_BUFFER_BASE ;
  else if(AxiDmaIns == &AxiDma1)
  TxBufferPtr = (u8 *)TX_BUFFER_BASE1 ;
  else if(AxiDmaIns == &AxiDma2)
  TxBufferPtr = (u8 *)TX_BUFFER_BASE2 ;
  else if(AxiDmaIns == &AxiDma3)
  TxBufferPtr = (u8 *)TX_BUFFER_BASE3 ;
  else
  {
  xil_printf("para err\r\n");
  return XST_FAILURE;
  }
  memset(TxBufferPtr,0,length);
  memcpy(TxBufferPtr,wBuffer,length);
  Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
  Status = XAxiDma_SimpleTransfer(AxiDmaIns,(UINTPTR) TxBufferPtr,
    length, XAXIDMA_DMA_TO_DEVICE);
//  xil_printf("status is %d",Status);
  if (Status != XST_SUCCESS)
  {
  return XST_FAILURE;
  }
    return XST_SUCCESS;
}
unsigned int dma_8_10b_recv(XAxiDma* AxiDmaIns,u8 *rBuffer)
{
  int Status;
  int i;
  int Length = 0 ;
  u8 *RxBufferPtr;
  Length = XAxiDma_ReadReg(AxiDmaIns->RegBase,0x58);
  if((AxiDmaIns == NULL)||(rBuffer == NULL))
  {
  xil_printf("recv Pointer err\r\n");
  return XST_FAILURE;
  }
  if (AxiDmaIns == &AxiDma)
  RxBufferPtr = (u8 *)RX_BUFFER_BASE ;
  else if(AxiDmaIns == &AxiDma1)
  RxBufferPtr = (u8 *)RX_BUFFER_BASE1 ;
  else if(AxiDmaIns == &AxiDma2)
  RxBufferPtr = (u8 *)RX_BUFFER_BASE2 ;
  else if(AxiDmaIns == &AxiDma3)
  RxBufferPtr = (u8 *)RX_BUFFER_BASE3 ;
  else
  {
  xil_printf("para err\r\n");
  return XST_FAILURE;
  }
  Xil_DCacheInvalidateRange(RxBufferPtr,MAX_PKT_LEN);
  for(i = 0; i < Length; i++)
  {
    rBuffer[i] = RxBufferPtr[i];
  }
  Status = XAxiDma_SimpleTransfer(AxiDmaIns,(UINTPTR) RxBufferPtr,
    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
  if (Status != XST_SUCCESS) {
  return XST_FAILURE;
  }
    return Length;
}

Demo部分如下

void Xdma_demo()
{
  int Status;
  int Length;
  u8 rBuffer[256] = {0};
  u8 wBuffer[256] = {0};
  int i;
  dma_init();
    for(i=0;i<256;i++)
    {
      wBuffer[i]=i+1;
    }
  dma_8_10b_send(&AxiDma,wBuffer,4);
    for(i=0;i<256;i++)
    {
      wBuffer[i]=i+2;
    }
  dma_8_10b_send(&AxiDma1,wBuffer , 16);
    for(i=0;i<256;i++)
    {
      wBuffer[i]=i+3;
    }
  dma_8_10b_send(&AxiDma2,wBuffer , 32);
    for(i=0;i<256;i++)
    {
      wBuffer[i]=i+4;
    }
  dma_8_10b_send(&AxiDma3,wBuffer , 32);
  while(1)
  {
  if(DMA0RxDone == 1)
  {
//    Status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR,0x58);
//      xil_printf("length2 is 0x%x\r\n",Status);
//      Status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR,0x28);
//      xil_printf("TX length2 is 0x%x\r\n",Status);
    Length = dma_8_10b_recv(&AxiDma,rBuffer);
    xil_printf("length DMA0 is 0x%x\r\n",Length);
    xil_printf("DATE: ");
    for(i=0; i<33; i++)
    {
    xil_printf("%x ",rBuffer[i]);
    }
    xil_printf("\r\n");
    DMA0RxDone = 0;
  }
  if(DMA1RxDone == 1)
  {
    Length = dma_8_10b_recv(&AxiDma1,rBuffer);
  xil_printf("length DMA1 is 0x%x\r\n",Length);
    xil_printf("DATE: ");
    for(i=0; i<33; i++)
    {
    xil_printf("%x ",rBuffer[i]);
    }
    xil_printf("\r\n");
    DMA1RxDone = 0;
  }
  if(DMA2RxDone == 1)
  {
    dma_8_10b_recv(&AxiDma2,rBuffer);
    xil_printf("DATE: ");
    for(i=0; i<33; i++)
    {
    xil_printf("%x ",rBuffer[i]);
    }
    xil_printf("\r\n");
    DMA2RxDone = 0;
  }
  if(DMA3RxDone == 1)
  {
    dma_8_10b_recv(&AxiDma3,rBuffer);
    xil_printf("DATE: ");
    for(i=0; i<33; i++)
    {
    xil_printf("%x ",rBuffer[i]);
    }
    xil_printf("\r\n");
    DMA3RxDone = 0;
  }
  }
}

 dma_8_10b_send()为封装后的发送函数,将宏定义的四路TXBUFFER和DMA匹配,实现DMA向设备发送数据的功能,并通过Xil_DCacheFlushRange()函数清理缓存

 dma_8_10b_recv()为封装后的接收函数,将宏定义的四路RXBUFFER和DMA匹配,实现DMA接收设备发送的数据的功能,数据长度通过读取DMA长度寄存器获得,这样的话更具有普遍性,同样通过Xil_DCacheFlushRange()函数清理缓存

 XAxiDma_SimpleTransfer是其原型,最后一个参数表示了传输方向收发的处理函数基本上是AXIDMA例程里的,仅对多余的接收完成标志位做了补充(多余的应该没了,也能在导入一个工程比较一下)哦对了很奇怪的一点是初始化的时候需要提前做一次接受的transfer,还没发现原因,在之后的挂载操作系统时也有一次类似的操作,当然linux下的transfer不是官方提供的,可能和裸板有些许差异,而且不提前做也没法从0x58的长度寄存器读取实际搬移数据的长度,另说了

 初始化部分是结合了之前章节讲的GPIO中断,没有用例程中的中断方式

先写这么多了,目前只了解到这些,SG模式的用法后续有机会更新吧

参考:芯片手册 原子开发手册 CSDN Xilinx社区等网络资源


相关文章
|
9月前
|
存储 芯片 SoC
ZYNQ程序固化
ZYNQ程序固化
|
9月前
|
Linux 开发工具 异构计算
【ZYNQ】QSPI Flash 固化程序全攻略
【ZYNQ】QSPI Flash 固化程序全攻略
1605 0
|
存储 安全 存储控制器
ZYNQ裸板:中断篇
中断对于单片机过来的我们来说,相对也算比较熟悉了,还是严谨一点从头开始说吧。中断是什么?是一种当满足要求的突发事件发生时通知处理器进行处理的信号。中断可以由硬件处理单元和外部设备产生,也可以由软件本身产生。对硬件来说,中断信号是一个由某个处理单元产生的异步信号,用来引起处理器的注意。对软件来说,中断还是一种异步事件,用来通知处理器需要改变代码的执行,当然,轮询所产生的中断的过程是同步的。
1049 0
ZYNQ裸板:中断篇
|
存储 Linux API
ZYNQ裸板:串口篇
使用 PS 的时候,通常会添加 UART 控制器,用于打印信息和调试代码。除此之外, PS 在和外部设备通信时,也会经常使用串口进行通信。先从UART控制器开始讲起吧,从简单的测试再到工程实例。
981 0
ZYNQ裸板:串口篇
|
开发工具 内存技术
zynq程序固化补充篇: 不切换启动模式强制烧写
使用SDK2018.2第一次进行烧写 Flash,在qspi模式下会报错,只有切换至jtag模式下才可以进行烧录,后续的再次烧录不会出现类似问题。但是调试的时候必须切回jtag模式(将BOOT MODE 5拉低才可以调试)
2420 1
zynq程序固化补充篇: 不切换启动模式强制烧写
|
存储 编解码 芯片
ZYNQ裸板:LHB155304-RT篇
1553总线是一种指令/响应式串行总线标准,除了作为美军标在国外广泛应用于军用飞机坦克、船舶、卫星、导弹等领域,在国内已得到了广泛的应用。抗干扰能力强实时性好,且拥有着双冗余备份设计,数据传输极为可靠。就我个人认知来看,常见的实现形式一种是通过专用的接口协议芯片,相对比较简单集成度高;另一种是IP核,非常考验逻辑和软件设计的功底。此次工程选用了前者,LHB15530接口芯片,在不改变原有传输方式的前提下,突破了原有的1Mb/s的传输速率,可达4Mb/s,也能满足绝大部分应用场景。
504 0
ZYNQ裸板:LHB155304-RT篇
|
传感器 定位技术 数据处理
ARM裸板开发——UART通信方式及使用(一)
ARM裸板开发——UART通信方式及使用
312 0
ARM裸板开发——UART通信方式及使用(一)
|
存储 Linux 数据安全/隐私保护
ZYNQ - 嵌入式Linux开发 -10- ZYNQ启动流程分析
ZYNQ - 嵌入式Linux开发 -10- ZYNQ启动流程分析
1218 0
ZYNQ - 嵌入式Linux开发 -10- ZYNQ启动流程分析
|
存储 安全 开发工具
ZYNQ裸板:程序固化篇
一般的调试中我们都是通过 JTAG 接口将 FPGA 配置文件和应用程序下载到 ZYNQ 器件中。同样的,我们也可以将尝试把程序存储在非易失性存储器中,在上电或者复位时让程序自动运行,这个过程需要启动引导程序( Boot Loader) 的参与
452 0
ZYNQ裸板:程序固化篇
|
芯片 内存技术
ZYNQ裸板:DDR篇
DDR是zynq的内存又可以叫做主存。是CPU能直接寻址的存储空间,没有DDR的话,运行内存只有很小的内部RAM,软件大小受限,也几乎没法跑操作系统。作为下一篇AXIDMA的基础知识提前做个准备,内容很简单这里就简单做一个读写测试,也可以当做验证下DDR是否都可以正常访问
638 0
ZYNQ裸板:DDR篇