您的位置:

AXI DMA的详细阐述

一、AXI DMA概述

AXI DMA是指Advanced eXtensible Interface Direct Memory Access,是Xilinx公司提供的基于AMBA协议的DMA核,用于处理高速数据传输,可以在FPGA内的不同IP核之间以及外部设备与FPGA之间进行数据传输。

AXI DMA支持以下特性:

  • 高吞吐量的数据传输
  • 支持多通道并发传输
  • 支持内存映射和文件操作等高级特性
  • 集成了CDO(Completion determined Operation),支持数据传输的补偿机制

二、AXI DMA SG

字面意义上,SG是指Scatter-Gather,一种数据传输的方式,也就是将数据在内存中分为若干帧,每帧数据可能存储在不同的物理地址中,传输时AXI DMA可以根据事先的描述从内存中逐个取出帧数据存到寄存器中进行发送,优点是可以降低在地址映射和DMA中间数据复制的开销以提升系统性能。以下是AXI DMA SG的使用示例:

 <!-- AXI DMA SG示例 -->
 <axidma_sg id="dma_sg">
  <-- AXI DMA配置 -->
  <register name="CONFIG" offset="0x00" value="0x00"/>
  <-- 内存地址1 -->
  <register name="SG_CTL" offset="0x00" value="0x80000000"/>
  <register name="SRC_ADDR_1" offset="0x04" value="0x10000000"/>
  <register name="DST_ADDR_1" offset="0x08" value="0x20000000"/>
  <register name="LENGTH_1" offset="0x0c" value="0x100"/>
  <-- 内存地址2 -->
  <register name="SG_CTL" offset="0x10" value="0x80000000"/>
  <register name="SRC_ADDR_2" offset="0x14" value="0x11000000"/>
  <register name="DST_ADDR_2" offset="0x18" value="0x21000000"/>
  <register name="LENGTH_2" offset="0x1c" value="0x200"/>
 </axidma_sg>

三、AXI DMA单向传输

AXI DMA支持单向数据传输,单向传输可以在数据方向锁定时消除请求延迟,提高传输效率。例如,以下代码示例演示了使用AXI DMA进行单项传输:

 <!-- AXI DMA单项传输示例 -->
 <axidma id="dma">
  <-- AXI DMA配置 -->
  <register name="CONFIG" offset="0x00" value="0x00"/>
  <register name="SRC_ADDR" offset="0x18" value="0x10000000"/>
  <register name="DST_ADDR" offset="0x20" value="0x20000000"/>
  <register name="LENGTH" offset="0x28" value="0x400"/>
 </axidma>

四、AXI DMA驱动

AXI DMA可以很方便地在Linux系统中使用,只要安装了相关的驱动程序即可,以下是在Linux系统中使用AXI DMA的驱动程序示例:

/* AXI DMA驱动示例 */
 #include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
 #include <linux/of_device.h>

 static struct dma_chan *dma_tx_ch;  // DMA通道
 static dma_addr_t dma_tx_addr;      // DMA内存映射的物理地址

 static int dma_init(void)
 {
  struct device_node *np = NULL;
  struct scatterlist sg_tx;
  struct dma_async_tx_descriptor *dma_tx_desc;

  np = of_find_compatible_node(NULL, NULL, "xlnx,axi-dma");
  if (!np) {
   pr_err("Failed to find DMA node\n");
   return -EINVAL;
  }

  dma_tx_ch = dma_request_chan(&(np->dev), "dma0");
  if (IS_ERR(dma_tx_ch)) {
   pr_err("DMA channel request failed\n");
   return -EFAULT;
  }
  /* 申请DMA内存 */
  dma_tx_addr = dma_map_single(&(np->dev), &dma_tx_buf, DMA_LEN, DMA_TO_DEVICE);

  sg_init_one(&sg_tx, dma_tx_addr, DMA_LEN);
  dma_tx_desc = dmaengine_prep_dma_memcpy(dma_tx_ch, dma_tx_addr, DMA_BUF_ADDR, DMA_LEN, DMA_PREP_INTERRUPT);
  if (!dma_tx_desc) {
   pr_err("DMA prep memcpy fail\n");
   return -EINVAL;
  }
  dma_submit_error dma_async_submit(dma_tx_desc);

  return 0;
 }

 static void dma_exit(void)
 {
  dmaengine_terminate_all(dma_tx_ch);
  dma_unmap_single(&dev, dma_tx_addr, DMA_LEN, DMA_TO_DEVICE);
  dma_release_channel(dma_tx_ch);
 }

 module_init(dma_init);
 module_exit(dma_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Xilinx Inc.");

五、AXI DMA SG BD

AXI DMA支持的另一个特性是SG BD模式。BD是指Buffer Descriptor,即缓冲区描述表。它基于SG模式,将传输参数和每个SG段所使用的缓冲区的物理地址以及传输完成后的信号等参数描述到BD表中,从而更好地管理传输。以下是AXI DMA SG BD的使用示例:

 <!-- AXI DMA SG BD示例 -->
 <axidma_sg_bd id="dma_sg_bd">
  <register name="CONFIG" offset="0x00" value="0x01"/>
  <-- BD号0 -->
  <register name="BD_ADDR_0" offset="0x00" value="0x50000000"/>
  <register name="BD_LENGTH_0" offset="0x04" value="0x400"/>
  <register name="BD_COMPLETED_0" offset="0x08" value="0x01"/>
  <-- BD号1 -->
  <register name="BD_ADDR_1" offset="0x10" value="0x51000000"/>
  <register name="BD_LENGTH_1" offset="0x14" value="0x800"/>
  <register name="BD_COMPLETED_1" offset="0x18" value="0x00"/>
 </axidma_sg_bd>

六、AXI DMA丢数据

在使用AXI DMA进行数据传输时,可能会由于各种原因导致数据丢失问题,这是非常常见的问题之一。下面提供几个丢失数据的原因和解决办法。

原因1:链路层丢包引起的数据丢失

解决方案:

  • 调整DMA传输速度以适应链路层速度
  • 优化DMA传输的协议
  • 加入FIFO等缓存进行中转

原因2:由于FIFO的填充导致数据丢失

解决方案:

  • 调整FIFO的大小以适应数据量的多少
  • 增大FIFO的读写速度或者增加多个FIFO,让数据同时进行写入和输出

七、AXI DMA循环传输

AXI DMA支持循环传输,即在数据传输完成后再返回之前的地址继续传输,这样可以实现循环使用内存和控制器,提高系统效率。以下是AXI DMA循环传输的使用示例:

 <!-- AXI DMA循环传输示例 -->
 <axidma_cyclic id="dma_cyclic">
  <-- AXI DMA配置 -->
  <register name="CONFIG" offset="0x00" value="0x02"/>
  <register name="SRC_ADDR" offset="0x04" value="0x10000000"/>
  <register name="DST_ADDR" offset="0x08" value="0x20000000"/>
  <register name="LENGTH" offset="0x0c" value="0x400"/>
 </axidma_cyclic>