当前位置: 首页 > news >正文

ZYNQB笔记(十六):AXI DMA 环路测试

版本:Vivado2020.2(Vitis)

任务:使用 PL 端的 AXI DMA IP 核实现对 DDR3 中数据的读取与写入,实现数据环回,具体流程为:

        PS 端产生测试数据并写入到 DDR3 中,然后 PL 端的 AXI DMA IP 核从 DDR3 中读取数据,将读取到的数据存储到 AXI Stream Data FIFO 中(模拟AXI Stream 外设)。然后再将 AXI Stream Data FIFO 中的数据写回到 DDR3 中。判断从 DDR3 中读取的数据和写入的数据是否一致。

目录

一、介绍

(1)DMA

(2)AXI DMA IP核

二、硬件设计

(1)整体系统框图

(2)AXI DAM IP 核配置

(3)ZYNQ配置

(4)其他配置

三、软件设计

(1)AXI DMA 编程顺序

(2)完整代码

四、效果


一、介绍

(1)DMA

       DMA(Direct Memory Access,直接存储器访问)是一种 硬件加速的数据传输技术,允许外设或存储单元之间 直接交换数据,无需CPU逐字节参与搬运,从而解放CPU算力。

        当使用DMA时,CPU向DMA控制器发出一个存储传输请求,当DMA控制器接收到请求就会将数据从源地址搬运到目的地址。在数据搬运过程中不占用CPU资源,CPU可以执行其它操作,当传输完成时DMA以中断的方式通知CPU

        ZYNQ 提供了两种 DMA ,一种是PS中的DMA控制器(DMAC) ,通过GP口与PL端连接;另一种是PL中的 AXI DMA IP核(软核)。由于 PS 端的 DMAC 必须通过驻留在内存中的 DMA 指令编程,这些程序往往由 CPU 运行,因此需要 部分的 CPU 参与,所以在使用 PS 端的 DMAC 搬运数据时,不能完全释放 CPU 的资源。因此本实验选择 PL 端的 AXI DMA,在 PL 中添加 AXI DMA IP 核,并利用 AXI_HP 接口完成高速的数据传输。

(2)AXI DMA IP核

        AXI DMA (Direct Memory Access,AXI 直接内存访问) 是 ZYNQ SoC 中用于高性能数据传输的IP核,通过AXI总线实现 PS 与 PL 之间的直接数据搬运,无需CPU介入。通过双通道传输实现高效数据搬运:

        AXI DMA 接口类型(简单 DMA):

接口类型协议方向主要用途典型连接对象

控制接口

AXI4-LiteCPU → DMA配置DMA控制寄存器(启动传输、设置地址、长度、中断使能等)处理器(如ARM Cortex、MicroBlaze)

内存接口

AXI4 Memory-MappedDMA ↔ DDR用于大数据量传输,有两个方向:
MM2S:从内存读取数据
S2MM:向内存写入数据
DDR3/DDR4、片上RAM(BRAM)

外设接口

AXI4-StreamDMA ↔ 外设

低延迟流式数据传输

Master:发送数据到外设(MM2S方向)
Slave:接收外设数据(S2MM方向)

FPGA逻辑(如FIFO、ADC/DAC、以太网MAC)、高速串行接口(PCIe)
中断接口中断信号 IRQDMA → CPU

通知CPU传输完成或错误事件

处理器(如ARM Cortex、MicroBlaze)

        在AXI DMA 中,MM2S(Memory-Mapped to Stream)和S2MM(Stream to Memory-Mapped)是两个核心数据传输方向,分别用于从内存到外设和从外设到内存的数据搬运。

MM2SS2MM
数据方向内存 → 外设(Stream)外设(Stream) → 内存
接口协议AXI4-MemoryMap → AXI4-StreamAXI4-Stream → AXI4-MemoryMap
典型场景发送数据(如网络包、视频输出)接收数据(如传感器采集、网络收包)
中断触发传输完成或错误时触发 mm2s_introut传输完成或错误时触发 s2mm_introu
控制寄存器需配置源地址、传输长度等需配置目的地址、传输长度等

 

        AXI DMA IP 核可在 AXI4 和 AXI4-Stream IP 接口之间提供高带宽直接内存访问。其可选的分散收集模式(SG)还可以从基于处理器的系统中卸载 CPU 的数据移动任务。初始化、状态和管理寄存器可通过 AXI4-Lite 从接口访问,下面 AXI DMA 的介绍为正点原子的资料切片:

二、硬件设计

(1)整体系统框图

        本次实验将 AXI Stream Data FIFO IP 核类比成带 AXI Stream 接口的外设,通过 AXI DMA IP 核实现 DDR3 和外设之间的数据搬运。

        前面介绍部分了解到 AXI DMA 有三种类型的接口,用于对 AXI DMA 配置的 AXI-Lite 接口,用于和 DDR3 内存做大量数据读写的存储器映射接口(AXI4 Memory Map Read / Write)以及和外设做数据传输的 AXI4-Stream 流接口(AXI4 Stream Master / Slave)。

        AXI DMA 的 AXI4-Lite 接口主要用于 PS 对 AXI DMA 的寄存器进行配置,而 AXI DMA 的存储器映射接口用于读写 DDR3 内存中的数据,这两个接口都涉及到 PL 和 PS 端的数据交互。PL 和 PS 交互的接口可 以使用 HP 或者 GP 接口,HP 可以比 GP 接口提供更高的带宽,由于 PS 对 AXI DMA 的配置数据量较少, 所以可以选择使用 GP 接口;而 AXI DMA 的存储器映射接口用于读写 DDR3 内存中的数据,数据量会比较 大,为了提高 AXI DMA 数据搬运的效率,这里选择使用 HP 接口。无论是选择使用 HP 或者 GP 接口,都需要通过 AXI Interconnect 模块进行转换,因此本次实验需要两个 AXI Interconnect 模块。AXI DMA 的 Stream 流接口直接和带 Stream 流接口的外设连接即可,如本章使用的 AXI Stream Data FIFO。

(2)AXI DAM IP 核配置

        具体配置如图所示, 注意 DMA 设置为直接寄存器模式 Direct Register Mode(Simple DAM),所以不启用SG模式。本例既读又写,就需要同时启用读(MM2S)、写(S2MM)通道。

配置参数说明
Enable Asynchronous Clocks允许 MM2S/S2MM/SG/Lite 接口时钟异步运行选项为无法设置,会根据 BD设计中该 IP核的时钟连线自动设置(比如 MM2S 和 S2MM 时钟为异步时就自动允许时钟异步运行)
Enable Scatter Gather Engine启用 SG模式

如果不启用,则 DMA 为直接寄存器模式 Direct Register Mode(Simple DAM)

Enable Micro DMA优化为精简 DMA使能后 IP核硬件资源占用更少,适用于少量数据的 DAM 传输
Width of Buffer Length Register缓冲区长度寄存器 宽度

该值确定 DMA 传输的最大数据包大小,计算公式:最大字节数=2^LengthWidth-1。

(width = 26 允许传输 67,108,863 字节) 

Address WidthAXI4 M MAP接口地址位宽

指 AXI DMA的 AXI4 Memory Map(MM)接口的地址总线位宽,决定了 DMA控制器可以访问的内存地址范围。

在 ZYNQ 中通常配置为 32位(7000系列)或 64位(UltraScale+)。

Enable read / write channel

启用 DMA 读/写通道按需设置,本例既读又写,就需要同时启用

(3)ZYNQ配置

       由(1)可知: PS 通过 GP 接口控制 DMA,而 DAM 使用 HP 接口与 PS 进行数据交互,所以配置 ZYNQ 时需要使用一个 GP(Master接口)和一个 HP接口。

        此外还使用到了 PL到PS中断、PL时钟、复位、UART。

(4)其他配置

        AXI Sream Data FIFO 用于模拟一个带流接口的外设,配置如图所示,直接保持默认即可,FIFO的深度为512,数据宽度是自动设置的。需要将AXI DMA的外设接口(Stream流接口)与 AXI Sream Data FIFO 的Stream流接口连接:

        DMA MM2S 方向的流接口,连接 FIFO 流接口(从),用于DMA向FIFO写数据。

        DMA S2MM 方向的流接口,连接 FIFO 流接口(主),用于FIFO向DMA传数据。

        此外因为 AXI DMA 用到了MM2S和S2MM两个通道,有两个中断输出,而 ZYNQ 的中断输入只有一个多位宽的中断输入端口,需要用到一个 Concat IP核(Concatenation,拼接,是Xilinx FPGA中提供的一个基础IP核,用于将多个输入信号按位拼接成一个更宽的输出信号,仅实现信号的物理连接)将 mm2s_introut、s2mm_introu 两个1位的中断输出信号按位拼接为一个两位的中断输出信号,给到 ZYNQ 中断输入端口

        最后整体 bd 设计部分如图所示:设计检查、Generate Output Products、 Create HDL Wrapper、管脚约束、Gnerate Bitstream、Export Hardware(包含比特流文件)、启动Vitis

三、软件设计

(1)AXI DMA 编程顺序

        下面是AXI DMA 使用手册中,以直接寄存器模式(简单模式)下 DMA 具体的使用方法,包含该模式的编程顺序介绍、MM2S、S2MM 通道的启动顺序:

总结一下:

AXI DMA编程顺序:
        Direct RegisterMode(简单DMA)此模式提供了在MM2s和s2MM通道上进行简单DMA传输的配置,只需较少的FPGA资源通过访问DMACR、源地址或者目的地址和长度寄存器发起DMA传输。当传输完成后,如果使能了产生中断输出,那么DMASR寄存器相关联的通道位会有效。       

MM2S 通道启动步骤

步骤操作步骤描述注意事项
1使能 MM2S通道- 通过 MM2S_DMACR 寄存器的 RS (Run/Stop) 位使能。
- 确保通道处于停止状态(RS=0)后再配置其他寄存器。
2根据需求,使能中断寄存器- 中断使能位在 MM2S_DMACR 寄存器中(如 IOC_IrqEn 用于传输完成中断)。
- 若需PS端响应中断,需配置GIC或处理器中断控制器。
3写一个有效的源地址到 MM2S_SA 寄存器- 地址需按数据位宽对齐(如32位数据需4字节对齐)。
- 若未使能 DRE(Data Realignment Engine),需严格对齐,否则可能读取错误数据。
4写传输的字节数到 MM2S_LENGTH 寄存器- 写入非0值(0无效),且必须 最后配置此寄存器
- 写入后DMA立即开始传输。
- 其他寄存器(如地址、控制)需在此前配置完成。

S2MM 通道启动步骤:

步骤操作步骤描述注意事项
1使能 S2MM通道- 通过 S2MM_DMACR 寄存器的 RS (Run/Stop) 位使能。
- 确保通道处于停止状态(RS=0)后再配置其他寄存器。
2根据需求,使能中断寄存器- 中断使能位在 S2MM_DMACR 寄存器中(如 IOC_IrqEn 用于传输完成中断)。
- 若需PS端响应中断,需配置GIC或处理器中断控制器。
3写一个有效的目的地址到 S2MM_DA 寄存器- 地址需按数据位宽对齐(如32位数据需4字节对齐)。
- 若未使能 DRE(Data Realignment Engine),需严格对齐,否则可能写入错误地址。
4写传输的字节数到 S2MM_LENGTH 寄存器- 写入非零值(0无效),且必须 最后配置此寄存器
- 写入后DMA立即开始传输。
- 其他寄存器(如地址、控制)需在此前配置完成。

(2)完整代码

        导入Xilinx的 axi_dma 的工程模板,选着简单DMA模式中断的示例,以简单DMA模式使用AXI DMA 的代码是基于这个模板实现的,方便参考与照搬套用。

#include "xil_printf.h"
#include "xparameters.h"
#include "xaxidma.h"
#include "xscugic.h"//======================宏定义=====================////DDR中的地址宏定义
#define DDR_BASE_ADDR   XPAR_PS7_DDR_0_S_AXI_BASEADDR	//DDR的基地址(在xparameters.h或lscript.ld查看)
#define MEM_BASE_ADDR	(DDR_BASE_ADDR + 0x01000000)	//DDR中存储数据的基地址(确保在堆栈已使用DDR范围之后,lscript.ld查看)
#define TX_BUFFER_BASE	(MEM_BASE_ADDR + 0x00100000)	//存储发送数据的基地址
#define RX_BUFFER_BASE	(MEM_BASE_ADDR + 0x00300000)	//存储接收数据的基地址#define RESET_TIMEOUT_COUNTER	10000	//复位时间计数
#define MAX_PKT_LEN				0x100	//发送包长度(测试数据个数,256)#define INTC_DEVICE_ID	XPAR_SCUGIC_SINGLE_DEVICE_ID	  		//中断控制器(GIC)ID
#define AXI_DMA_ID  	XPAR_AXIDMA_0_DEVICE_ID       			//AXI DMA器件ID
#define TX_INTR_ID		XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID//DMA发送通道中断号(mm2s_introut)
#define RX_INTR_ID		XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID//DMA通道通道中断号(s2mm_introut)//===================函数变量声明===================//XScuGic Intc;		//中断控制器驱动实例
XAxiDma AxiDma;		//AXI DMA驱动实例volatile int RxDone;	//接收完成标志
volatile int TxDone; 	//发送完成标志
volatile int Error; 	//传输出错标志
u8 *TxBufferPtr = (u8 *)TX_BUFFER_BASE;	//定义指针并指向发送缓冲区
u8 *RxBufferPtr = (u8 *)RX_BUFFER_BASE;	//定义指针并指向接收缓冲区void Trans_Test();						//数据传输测试
void Test_Check();						//传输测试检查
void AXI_DMA_Init();					//AXI DMA初始化
void Tx_IntrHandler(void *Callback);	//中断函数(mm2s_introut)
void Rx_IntrHandler(void *Callback);	//中断函数(s2mm_introut)
void Set_Intr_Sys(XScuGic *GicInstPtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);	//建立中断系统//=====================主函数======================//
int main()
{xil_printf("AXI_DMA Loop Test \r\n");//AXI_DMA初始化AXI_DMA_Init();//建立中断系统Set_Intr_Sys(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);//进行数据传输测试Trans_Test();return 0;
}//==================传输数据测试==================//
void Trans_Test()
{TxDone = 0;RxDone = 0;Error  = 0;//初始化发送缓冲区数据(每次递增1)u8 value = 0x0;for (int i=0; i<MAX_PKT_LEN; i++){TxBufferPtr[i] = value;		//写入数据(TxBufferPtr[i] 指针偏移计算:等价于 *(TxBufferPtr+i),按 1字节步长计算地址)value = (value + 1) & 0xFF; //取低8位(1字节)}//刷新 Data Cache(将指定内存区域数据从 CPU数据缓存 Data Cache刷入主存 DDR,确保 DMA或外设访问的是最新数据)Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);//简单模式下DMA数据传输(DMA指针, 起始地址, 数据长度, 传输方向)//DMA将数据从DDR搬运到外设XAxiDma_SimpleTransfer(&AxiDma, (UINTPTR) TxBufferPtr, MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);//等待 DMA 搬运完从 DDR3 到 AXI Stream Data FIFO 的数据while(!TxDone && !Error) //等待至发送结束(或传输出错)if(Error){xil_printf("Tx Error!!\r\n");return;}//DMA将数据从外设搬运到DDRXAxiDma_SimpleTransfer(&AxiDma, (UINTPTR) RxBufferPtr, MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);//等待 DMA 搬运完从 AXI Stream Data FIFO 到 DDR3  的数据while(!RxDone && !Error)//等待至接收结束(或传输出错)if(Error){xil_printf("Rx Error!!\r\n");return;}//测试数据检查Test_Check();
}//==================传输测试检查==================//
void Test_Check()
{int check_error = 0;int Tx,Rx;//比较两个缓冲区数据(全部相同时测试成功)for (int i=0; i<MAX_PKT_LEN; i++){Tx = TxBufferPtr[i];Rx = RxBufferPtr[i];xil_printf("Tx: %x	Rx: %x \r\n", Tx, Rx);if(Tx != Rx)check_error = 1;}if(check_error)xil_printf("AXI_DMA Test Failed!!");elsexil_printf("AXI_DMA Test Passed!!");
}//==================AXI DMA初始化==================//
void AXI_DMA_Init()
{//根据DMA器件ID查找配置信息,初始化DMAXAxiDma_Config *Config;Config = XAxiDma_LookupConfig(AXI_DMA_ID);int state = XAxiDma_CfgInitialize(&AxiDma, Config);if(state != XST_SUCCESS) {xil_printf("Failed to Initial AXI_DMA\r\n");}//(可选)检查DMA配置模式if(!XAxiDma_HasSg(&AxiDma)){ xil_printf("AXI_DMA configured as Simple mode\r\n"); }
}//===========中断处理函数(s2mm_introut)===========//
void Tx_IntrHandler(void *Callback)
{u32 IrqStatus;int TimeOut;XAxiDma *AxiDmaInst = (XAxiDma *)Callback;//获取中断状态IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);//确定中断类型XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);//中断为传输出错if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {//传输出错标志位置1Error = 1;//持续范围直到DMA复位结束XAxiDma_Reset(AxiDmaInst);TimeOut = RESET_TIMEOUT_COUNTER;while (TimeOut) {if(XAxiDma_ResetIsDone(AxiDmaInst)) {break;}TimeOut -= 1;}}//中断为传输完成if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK))TxDone = 1;//接收完成标志位置1
}//===========中断处理函数(mm2s_introut)===========//
void Rx_IntrHandler(void *Callback)
{u32 IrqStatus;int TimeOut;XAxiDma *AxiDmaInst = (XAxiDma *)Callback;//获取中断状态IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);//确定中断类型XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);//中断为传输出错if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {//传输出错标志位置1Error = 1;//持续范围直到DMA复位结束XAxiDma_Reset(AxiDmaInst);TimeOut = RESET_TIMEOUT_COUNTER;while (TimeOut) {if(XAxiDma_ResetIsDone(AxiDmaInst)) {break;}TimeOut -= 1;}}//中断为传输完成if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK))RxDone = 1;//接收完成标志位置1
}//===================建立中断系统==================//
/* 建立 AXI DMA中断系统,* @param GicInstPtr 是指向 XScuGic 驱动实例的指针* @param AxiDmaPtr  是指向 AXI DMA 实例的指针* @param TxIntrId	  是 DMA:mm2s_introut中断ID* @param RxIntrId	  是 DMA:s2mm_introut中断ID*/
void Set_Intr_Sys(XScuGic *GicInstPtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{//根据中断控制器ID,查找GIC配置信息XScuGic_Config * IntcConfig;IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);//初始化中断控制器驱动XScuGic_CfgInitialize(GicInstPtr, IntcConfig, IntcConfig->CpuBaseAddress);//设置并打开中断异常处理功能Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,GicInstPtr);//为DMA:mm2s_introut、s2mm_introut中断设置中断处理函数XScuGic_Connect(GicInstPtr, TxIntrId,(Xil_ExceptionHandler)Tx_IntrHandler, AxiDmaPtr);XScuGic_Connect(GicInstPtr, RxIntrId,(Xil_ExceptionHandler)Rx_IntrHandler, AxiDmaPtr);//设置中断优先级、触发类型(优先级从最高0开始步长8最大为248,0xA0=160优先级中等;触发类型:上升沿)XScuGic_SetPriorityTriggerType(GicInstPtr, TxIntrId, 0xA0, 0x3);XScuGic_SetPriorityTriggerType(GicInstPtr, RxIntrId, 0xA0, 0x3);//使能处理器中断Xil_ExceptionEnable();//使能来自于AXI DMA器件的两个中断XScuGic_Enable(GicInstPtr, TxIntrId);XScuGic_Enable(GicInstPtr, RxIntrId);//使能DMA mm2s_introut、s2mm_introut 所有中断(传输完成、出错...)XAxiDma_IntrEnable(AxiDmaPtr, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);XAxiDma_IntrEnable(AxiDmaPtr, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
}

四、效果

        下载后,可以看到打印出了“AXI_DMA configured as Simple mode”,说明AXI DMA是以简单DMA的模型运行的。后面立刻开始打印数据,可以看到发送和接收的数据是一样的,最后也答应出了“AXI_DMA Test Passed!!”,说明测试通过。

放在最后:

        当然除了用 AXI Sream Data FIFO 模拟一个带 Stream 流接口的外设之外,要实现 DMA 数据回环,也可以直接将 AXI DMA 的 的 S2MM 方向的Stream流接口直接接到MM2S 方向的Stream流接口。当 DMA 接收完自己发出的 Stream 流数据后,s2mm_introut 会产生一个接收完成的中断,此时PS端就可以将数据写回DDR了。

相关文章:

  • 开发板型号 ESP32-DevKitC-32模块型号 ESP32-WROOM-32 和主控芯片 ESP32-D0WDQ6-V3
  • 【AI论文】仅通过一个训练样本对大型语言模型进行推理的强化学习
  • RocketMQ常见面试题一
  • Python PyTorch库【机器学习框架】全面深入讲解与实践 入选【全站综合热榜第四】【领域内容榜第一】
  • dify+ollama+知识库 部署
  • 【树莓派Pico FreeRTOS】-FreeRTOS-SMP移植
  • 第十六届 -- 蓝桥杯Web开发大学组省赛个人复盘
  • [FPGA 官方 IP] Binary Counter
  • 编程题python常用技巧-持续
  • 第 11 届蓝桥杯 C++ 青少组中 / 高级组省赛 2020 年真题,选择题详细解释
  • 【笔记】深度学习模型训练的 GPU 内存优化之旅③:内存交换篇
  • 如何降低LabVIEW开发费用
  • 自动剪辑批量混剪视频过原创软件工具视频帧级处理技术实践批量截图解析
  • Leetcode刷题记录25——合并区间
  • 移动光猫 UNG853H 获取超级管理员账号密码
  • 一键解放双手,操作丝滑起飞!
  • Vue3 + OpenLayers 企业级应用进阶
  • 【浅尝Java】Java简介第一个Java程序(含JDK、JRE与JVM关系、javcdoc的使用)
  • matlab 中function的用法
  • 网络分析/
  • 特朗普宣布提名迈克·沃尔兹为下一任美国驻联合国大使
  • 乌美矿产协议文本公布,明确乌收益及协议优先级
  • 受天气等影响SC8041航班三次备降延误超12小时,山航致歉
  • “译通天下·言立寰宇”:华东师大翻译家的精神传承
  • 北方旱情持续,水利部:大中型灌区春灌总体有保障
  • 市场监管总局:2024年查办商标、专利等领域违法案件4.4万件