1. PS端DMA基础概念与优势解析
在Zynq SoC架构中,DMA(直接内存访问)控制器分为PL(可编程逻辑)端和PS(处理系统)端两种实现方式。PS端DMA是固化在Zynq硬件中的专用DMA控制器,与需要通过HDL代码在PL端实现的软核DMA有着本质区别。
关键区别:PS端DMA是硬连线(hardwired)的硬件模块,而PL端DMA是通过逻辑资源(LUT/FF/BRAM)综合出来的可配置模块。
PS端DMA的核心优势体现在三个方面:
- 零PL资源消耗:不占用宝贵的可编程逻辑资源,在FPGA资源紧张的项目中尤为重要
- 确定性延迟:硬件固化实现保证了稳定的传输性能
- 开发便捷性:Xilinx SDK提供完整的驱动支持,无需RTL开发
实测数据显示,在Zynq-7000系列器件上,PS端DMA控制器可提供:
- 8个独立通道
- 每个通道支持内存到内存、内存到外设、外设到内存三种传输模式
- 最大传输带宽可达1.2GB/s(AXI_HP接口全速运行时)
2. PS端DMA典型应用场景
2.1 低速外设数据搬运
对于SPI、I2C、UART等低速串行接口,PS端DMA是最佳选择。以SPI为例,配置DMA传输的典型流程:
- 初始化SPI控制器(设置时钟、模式等参数)
- 配置DMA源地址(SPI数据寄存器)和目标地址(DDR内存)
- 设置传输长度和突发大小
- 启动DMA传输
- 通过中断或轮询方式检测传输完成
c复制// 示例:SPI接收数据到DDR的DMA配置
XDmaPs_Cmd spi_dma_cmd;
spi_dma_cmd.ChanCtrl.SrcBurstSize = 4; // 4字突发
spi_dma_cmd.ChanCtrl.DstBurstSize = 16; // 16字突发
spi_dma_cmd.BD.SrcAddr = (u32)&SPI->DR; // SPI数据寄存器地址
spi_dma_cmd.BD.DstAddr = (u32)dma_buffer; // DDR目标地址
spi_dma_cmd.BD.Length = 1024; // 传输1KB数据
XDmaPs_Start(&DmaInst, DMA_CHANNEL, &spi_dma_cmd, 0);
2.2 算法加速的数据预处理
在异构计算场景中,PS端DMA常作为PL加速器的数据搬运助手:
- 通过PS端DMA将传感器数据从外设搬运到DDR
- PL端DMA从DDR读取数据送入加速IP
- 加速结果通过PL端DMA写回DDR
- PS端DMA将结果传输到输出设备
这种分工模式充分发挥了PS端DMA在低速传输和PL端DMA在高速传输上的各自优势。
2.3 内存间数据搬移
PS端DMA支持内存到内存的传输,适用于:
- 数据缓冲区搬移
- 内存数据重组
- 零拷贝数据传输
3. PS端DMA驱动开发详解
3.1 驱动API核心函数解析
Xilinx SDK提供的DMA驱动包含以下关键函数:
| 函数原型 | 功能描述 | 参数说明 |
|---|---|---|
XDmaPs_CfgInitialize |
DMA控制器初始化 | InstPtr: DMA实例指针 Config: 配置结构体 EffectiveAddr: 基地址 |
XDmaPs_Start |
启动DMA传输 | Channel: 通道号(0-7) Cmd: 传输命令结构体 HoldDmaProg: 是否保持DMA程序 |
XDmaPs_SetDoneHandler |
设置传输完成回调 | DoneHandler: 回调函数指针 CallbackRef: 用户自定义参数 |
3.2 典型开发流程
-
硬件配置:
- 在Vivado中确认DMA控制器已使能
- 分配适当的AXI接口带宽
-
软件初始化:
c复制XDmaPs DmaInst;
XDmaPs_Config *DmaCfg = XDmaPs_LookupConfig(DMA_DEVICE_ID);
XDmaPs_CfgInitialize(&DmaInst, DmaCfg, DmaCfg->BaseAddress);
- 传输配置:
c复制XDmaPs_Cmd dma_cmd;
memset(&dma_cmd, 0, sizeof(XDmaPs_Cmd));
dma_cmd.BD.SrcAddr = src_addr;
dma_cmd.BD.DstAddr = dst_addr;
dma_cmd.BD.Length = data_len;
dma_cmd.ChanCtrl.SrcBurstSize = XDMAPS_BURST_SIZE_16;
dma_cmd.ChanCtrl.DstBurstSize = XDMAPS_BURST_SIZE_16;
- 启动传输:
c复制int status = XDmaPs_Start(&DmaInst, channel_num, &dma_cmd, 0);
if (status != XST_SUCCESS) {
xil_printf("DMA start failed\n");
}
4. 性能优化与问题排查
4.1 性能优化技巧
-
突发传输配置:
- 合理设置
SrcBurstSize和DstBurstSize - 典型值:内存端16字,外设端4字
- 合理设置
-
缓存一致性处理:
c复制Xil_DCacheFlushRange(dma_buffer, buffer_size); // 写入前刷新 Xil_DCacheInvalidateRange(dma_buffer, buffer_size); // 读取前无效化 -
通道优先级设置:
- 通过
XDmaPs_SetChannelPriority()调整关键通道优先级
- 通过
4.2 常见问题排查
-
传输停滞:
- 检查AXI互联矩阵配置
- 验证源/目标地址是否对齐(建议32字节对齐)
-
数据错误:
- 确认缓存一致性操作
- 检查外设时钟域与DMA时钟域关系
-
性能不达标:
- 使用AXI_HP接口而非AXI_GP接口
- 增大突发长度(最大256字节)
5. 进阶应用:DMA链式传输
PS端DMA支持描述符链式传输,适合处理分散-聚集(scatter-gather)场景:
- 创建BD(Buffer Descriptor)链表:
c复制XDmaPs_Cmd bd_list[3];
// 初始化每个BD...
bd_list[0].BD.NextBdPtr = (u32)&bd_list[1];
bd_list[1].BD.NextBdPtr = (u32)&bd_list[2];
bd_list[2].BD.NextBdPtr = 0; // 链表结束
- 启动链式传输:
c复制XDmaPs_Start(&DmaInst, channel_num, &bd_list[0], 0);
这种模式特别适合处理:
- 非连续内存区块传输
- 多缓冲区数据收集
- 流式数据处理管道
在实际视频处理项目中,我曾使用链式DMA将摄像头采集的YUV数据分别传输到三个不同的处理缓冲区,减少了50%的内存拷贝开销。关键在于合理设置每个BD的传输长度和地址增量,使硬件能自动处理数据流的分割与重组。