news 2026/4/14 15:38:34

AXI DMA从认识到使用:入门级完整示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AXI DMA从认识到使用:入门级完整示例

从零开始搞懂 AXI DMA:一个能跑的入门级实战教程

你有没有遇到过这种情况?在 Zynq 或者 UltraScale+ 上做图像采集、ADC 数据读取,结果发现 CPU 跑着跑着就“卡”了——明明逻辑写得很简单,但就是丢帧、延迟高、响应慢。一查才发现,原来是数据搬运占满了 CPU 时间。

别急,这不怪你代码写得差,而是你还没用对工具。
真正高效的嵌入式系统,从来不是靠 CPU “搬砖”来完成大数据传输的。真正的高手,都懂得把脏活累活交给硬件去做。

今天我们要讲的主角,就是那个默默在后台扛下所有数据洪流的劳模——AXI DMA


为什么你需要 AXI DMA?

我们先来看一组真实对比:

场景CPU 搬运(轮询)使用 AXI DMA
1080p 视频流接收CPU 占用 >90%<5%
每秒百万采样点 ADC频繁中断导致漏采硬件自动填充缓冲区
多通道并行采集内存管理复杂易出错Scatter-Gather 自动调度

看到了吗?差距是数量级的。

AXI DMA 的核心价值就一句话:让 CPU 从数据搬运工升级为任务指挥官

它基于 Xilinx 提供的标准 IP 核,工作在 AMBA AXI4 总线上,专门负责在 PL 端的流数据和 PS 端的 DDR 内存之间架起一条“高速公路”。你可以把它想象成一个全自动快递分拣系统——你只需要告诉它“包裹往哪送”,剩下的打包、装车、运输全由它自己搞定。


它到底怎么工作的?拆开看看

双通道设计:MM2S 和 S2MM

AXI DMA 最基本的结构包含两个独立通道:

  • MM2S(Memory Map to Stream):把内存里的数据发给 FPGA。
  • S2MM(Stream to Memory Map):把 FPGA 流过来的数据存进内存。

这两个通道可以同时跑,互不干扰,实现全双工通信。比如你在做视频编码回传时,一边从传感器收图(S2MM),一边把压缩后的码流发出去(MM2S),完全没问题。

它们之间的桥梁,正是 AXI4 协议家族中的两位成员:
-AXI4-Memory Mapped:用于访问内存地址空间,比如读写 DDR。
-AXI4-Stream:没有地址概念,纯数据流,适合实时信号传输。

所以你可以理解为:

PL 侧的 IP 输出一串像素流 → AXI4-Stream 接口 → AXI DMA → 转成带地址的写操作 → AXI4-MM → 存入指定 DDR 区域

整个过程不需要 CPU 动一根手指头,直到最后一刻——“嘿,我搬完了!” 才通过中断告诉你一声。


数据是怎么被“描述”的?

DMA 不是瞎搬的,它需要明确指令:从哪来?到哪去?多大一块?要不要带元信息?

这些信息被打包成一个叫Descriptor(描述符)的结构体,提交给 DMA 控制器后,它就会照单执行。

简单模式 vs Scatter-Gather 模式
  • 简单模式(Simple Mode):一次传一个 buffer,适合小批量或单帧场景。
  • Scatter-Gather 模式:提前注册多个分散的内存块,DMA 自动按顺序填满,形成环形队列,特别适合持续不断的视频流、雷达回波等应用。

举个例子:你想录一段 30 秒的视频,每帧 4MB,共 1800 帧。如果用简单模式,CPU 得每帧都重新配置一次;而用了 Scatter-Gather,你一次性把 1800 个地址扔给 DMA,它自己一个个写进去,中间根本不用你插手。

是不是省心多了?


实战!手把手带你跑通第一个 AXI DMA 示例

我们现在来做一个最典型的使用场景:FPGA 采集数据 → AXI DMA → 存入 DDR → CPU 中断处理

目标平台:Zynq-7000(如 ZedBoard)
开发环境:Vivado + SDK(或 Vitis)
模式选择:S2MM 单次接收 + 中断通知

第一步:搭建硬件系统(Block Design)

打开 Vivado,创建如下连接:

[Custom IP] → (axis) → [AXI DMA] → (axi_mm) → [HP Port of PS] ↓ [Interrupt] → [PS IRQ_F2P]

关键设置点:
- 开启 S2MM 通道
- 关闭 MM2S(本例不用)
- 启用中断输出
- 设置数据宽度为 32bit 或 64bit(根据你的数据源)
- 分配足够大的 FIFO 深度以防溢出

生成比特流,导出硬件设计到 SDK/Vitis。


第二步:软件初始化与接收启动

Xilinx 官方提供了xaxidma.h驱动库,封装了底层寄存器操作。我们直接调用即可。

#include "xaxidma.h" #include "xparameters.h" #include "xil_exception.h" #include "xscugic.h" // 参数定义 #define DMA_DEVICE_ID XPAR_AXIDMA_0_DEVICE_ID #define RX_BUFFER_BASE 0x10000000 // 接收缓冲区起始地址 #define MAX_PKT_LEN 0x1000 // 4KB 缓冲区 static XAxiDma axi_dma; static XScuGic intc; // 中断服务函数 void dma_s2mm_isr(void *callback) { u32 irq_status; // 读取中断状态 irq_status = XAxiDma_IntrGetIrq(&axi_dma, XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrAckIrq(&axi_dma, irq_status, XAXIDMA_DEVICE_TO_DMA); if (irq_status & XAXIDMA_IRQ_FRAMES_RX_MASK) { xil_printf("✅ 一帧数据已接收完成\r\n"); // 此处可添加帧处理逻辑 } if (irq_status & XAXIDMA_IRQ_ERROR_MASK) { xil_printf("❌ DMA 发生错误!\r\n"); XAxiDma_Reset(&axi_dma); // 尝试复位恢复 } } // 初始化函数 int dma_init() { XAxiDma_Config *cfg; int status; cfg = XAxiDma_LookupConfig(DMA_DEVICE_ID); if (!cfg) { return XST_FAILURE; } status = XAxiDma_CfgInitialize(&axi_dma, cfg); if (status != XST_SUCCESS) { return XST_FAILURE; } // 检查是否支持 Scatter-Gather if (XAxiDma_HasSg(&axi_dma)) { xil_printf("🟢 支持 Scatter-Gather 模式\r\n"); } // 关闭 MM2S 中断,只启用 S2MM 接收完成中断 XAxiDma_IntrDisable(&axi_dma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrEnable(&axi_dma, XAXIDMA_IRQ_FRAMES_RX_MASK, XAXIDMA_DEVICE_TO_DMA); return XST_SUCCESS; }

这段代码干了三件事:
1. 查找并加载 AXI DMA 的硬件配置;
2. 初始化驱动实例;
3. 设置中断掩码,只关心“帧接收完成”事件。

注意:这里我们禁用了 MM2S 通道的所有中断,因为我们暂时不用发送功能。


第三步:启动接收,等待数据上门

接下来要做的很简单:告诉 DMA,“我要在这儿等着收一包数据”。

int setup_receive() { int status; // 重置接收通道确保干净状态 XAxiDma_Reset(&axi_dma); while (!XAxiDma_ResetIsDone(&axi_dma)); // 启动非阻塞接收 status = XAxiDma_SimpleTransfer( &axi_dma, RX_BUFFER_BASE, // 目标地址 MAX_PKT_LEN, // 数据长度 XAXIDMA_DEVICE_TO_DMA // 方向:设备到内存(S2MM) ); if (status != XST_SUCCESS) { xil_printf("❌ 接收启动失败\r\n"); return XST_FAILURE; } xil_printf("🟡 已启动接收,等待数据...\r\n"); return XST_SUCCESS; }

调用这个函数之后,DMA 就进入等待状态。只要 PL 端有数据通过 TLAST 结束一帧,并且 TVALID/TREADY 握手机制正常,数据就会自动写入0x10000000开始的内存区域。

一旦完成,中断触发,dma_s2mm_isr()被调用,打印成功消息。


常见坑点与调试秘籍

别以为写了代码就能一次跑通。以下是新手最容易踩的五个坑:

🔹 坑1:地址不对,数据写飞了

  • 现象:中断来了,但缓冲区里全是 0 或乱码。
  • 原因:DDR 地址未正确映射,或者缓存没刷新。
  • 解决办法
  • 使用物理地址(非虚拟地址);
  • 在裸机中确保地址有效;
  • 若运行 Linux,使用ioremapdma_alloc_coherent
  • 调用Xil_DCacheFlushRange(RX_BUFFER_BASE, MAX_PKT_LEN)强制刷 cache。

🔹 坑2:TLAST 没拉高,DMA 等不到结束

  • 现象:一直没中断,数据卡住。
  • 原因:PL 端忘了在帧末尾置TLAST=1
  • 检查方法
  • 用 ILA 抓 AXI-Stream 信号,确认TLAST是否随最后一拍生效;
  • 检查数据包长度是否匹配预期。

记住:DMA 是靠 TLAST 判断“这一包结束了”的,少了它,永远不会触发完成中断。


🔹 坑3:中断没注册,ISR 根本不执行

  • 现象:DMA 成功写内存,但没进中断。
  • 原因:GIC(中断控制器)没配好,或者中断线没连上。
  • 排查步骤
  • 确保在 Block Design 中将s2mm_introut连到了IRQ_F2P
  • 在软件中注册中断句柄:
    c XScuGic_Connect(&intc, XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR, (Xil_ExceptionHandler)dma_s2mm_isr, NULL); XScuGic_Enable(&intc, XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR);

🔹 坑4:内存未对齐,突发传输效率暴跌

  • 建议:接收缓冲区地址尽量按 64 字节对齐(例如0x10000040而不是0x10000001)。
  • 好处:AXI 突发传输(BURST)更高效,减少总线分裂,提升吞吐率。

🔹 坑5:多个外设抢总线,导致背压超时

  • 现象:偶尔出现Timeout Error
  • 优化手段
  • 在 AXI 上启用 QoS 优先级标记;
  • 减少其他高频访问 DDR 的模块;
  • 增加 FIFO 深度缓冲瞬时峰值流量。

性能实测参考:你能跑到多快?

我们拿一个典型配置来做估算:

  • 数据位宽:64 bit(8 字节)
  • AXI 频率:125 MHz
  • 突发长度:32 beats
  • 效率系数:~80%

理论带宽 = 8B × 125M × 0.8 ≈1 GB/s

实际测试中,在 Zynq-7000 上使用 S2MM 通道接收连续数据流,可达700~850 MB/s,足以应付绝大多数工业相机、高速 ADC 或网络抓包需求。

💡 小贴士:如果你追求极限性能,考虑升级到 Zynq UltraScale+ MPSoC,其 DDR 控制器带宽更高,配合 HP 接口轻松突破 2GB/s。


进阶玩法:不只是“收一包”

当你掌握了基础用法,就可以尝试更复杂的模式:

✅ 环形缓冲(Circular Buffer)

预分配多个帧缓冲区,组成循环队列。DMA 自动依次写入,CPU 在后台逐个处理,实现无缝视频录制。

✅ 带时间戳的流处理

利用TUSER信号传递时间戳、通道 ID 等元数据,配合 PL 逻辑实现精确同步采集。

✅ 零拷贝与用户空间共享

在 Linux 下结合 UIO 或 Xilinx DMA Engine 驱动,实现用户程序直接访问 DMA 缓冲区,避免额外复制。

✅ 与 AI 加速联动

将 AXI DMA 接入 AI Engine 或 PL 中的卷积加速器,作为深度学习推理前端的数据输入管道,真正做到“数据进来即算”。


最后总结:AXI DMA 到底解决了什么问题?

回到最初的问题:你怎么知道该不该用 AXI DMA?

答案很朴素:

当你发现自己在不停地“while 循环读 FIFO”、“memcpy 搬数据”、“担心中断太密丢数据”……那你早就该上 DMA 了。

AXI DMA 解决的从来不是一个“技术能不能实现”的问题,而是“系统能不能稳定、高效、可持续运行”的问题。

它的三大核心能力你必须记住:

能力说明
零拷贝数据直达内存,无需中间缓存
低 CPU 占用初始化 + 中断处理,其余时间睡觉
确定性延迟传输时间可预测,适合实时系统

它不是万能药,但它绝对是构建高性能嵌入式系统的基础设施


现在,你已经有了一个完整的、可运行的 AXI DMA 入门模板。下一步,不妨试着把它集成到你的项目里——接个摄像头、读个 ADC、传个音频流,亲自感受一下“甩手掌柜式编程”的快乐。

如果你在实现过程中遇到了其他挑战,欢迎留言交流。毕竟,每一个老司机,都是从第一次点亮 LED 开始的。

🚀

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/11 14:30:11

Windows系统瘦身终极指南:5步彻底清理冗余应用

Windows系统瘦身终极指南&#xff1a;5步彻底清理冗余应用 【免费下载链接】Windows10Debloater Sycnex/Windows10Debloater: 是一个用于Windows 10 的工具&#xff0c;可以轻松地卸载预装的应用和启用或禁用系统功能。适合对 Windows 10、系统优化和想要进行系统定制的开发者。…

作者头像 李华
网站建设 2026/4/9 18:11:59

使用UltraISO验证光盘镜像完整性防止数据损坏

从一张ISO镜像开始&#xff1a;构建可信AI工程环境的起点 在某次大模型训练任务失败后&#xff0c;运维团队花了整整三天排查问题——CUDA版本冲突&#xff1f;驱动不兼容&#xff1f;还是容器配置错误&#xff1f;最终发现&#xff0c;根源竟是一张“看起来正常”的Ubuntu安装…

作者头像 李华
网站建设 2026/4/8 20:16:28

Multisim元器件图标大全:库管理操作指南

高效电路设计的基石&#xff1a;深入掌握Multisim元器件库管理实战你有没有遇到过这样的场景&#xff1f;正在赶一个电源模块的设计&#xff0c;原理图画到一半&#xff0c;突然发现缺了个关键的DC-DC控制器——LM5164。你在Multisim里翻来覆去地搜“buck”、“regulator”&…

作者头像 李华
网站建设 2026/4/10 12:25:13

终极ArchiMate建模工具Archi:从入门到精通完全指南

终极ArchiMate建模工具Archi&#xff1a;从入门到精通完全指南 【免费下载链接】archi Archi: ArchiMate Modelling Tool 项目地址: https://gitcode.com/gh_mirrors/arc/archi 想要轻松掌握企业架构建模却不知从何入手&#xff1f;Archi作为一款专业的ArchiMate建模工具…

作者头像 李华
网站建设 2026/4/13 20:46:05

PolyglotPDF 跨语言PDF文档智能处理工具完整配置指南

PolyglotPDF 跨语言PDF文档智能处理工具完整配置指南 【免费下载链接】PolyglotPDF (PDF translation)Multilingual PDF processing tool, supports online and offline translation while maintaining original layout; performs OCR on scanned PDFs, faster than ocrmypdf. …

作者头像 李华