news 2026/2/22 7:12:05

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/2/19 21:34:12

ms-swift支持模型元数据管理便于检索追溯

ms-swift 支持模型元数据管理便于检索追溯 在大模型研发进入工业化阶段的今天&#xff0c;企业面临的不再是“能不能训出来”的问题&#xff0c;而是“如何高效管理数百个模型版本、快速定位最优部署方案&#xff0c;并确保每一次迭代都可追溯、可复现”。当一个团队同时运行着…

作者头像 李华
网站建设 2026/2/20 21:18:34

卡卡字幕助手:告别繁琐字幕制作,AI智能赋能视频创作新时代

卡卡字幕助手&#xff1a;告别繁琐字幕制作&#xff0c;AI智能赋能视频创作新时代 【免费下载链接】VideoCaptioner &#x1f3ac; 卡卡字幕助手 | VideoCaptioner - 基于 LLM 的智能字幕助手&#xff0c;无需GPU一键高质量字幕视频合成&#xff01;视频字幕生成、断句、校正、…

作者头像 李华
网站建设 2026/2/21 1:17:34

Thief摸鱼神器:如何在工作中实现完美隐蔽式放松

Thief摸鱼神器&#xff1a;如何在工作中实现完美隐蔽式放松 【免费下载链接】Thief 一款创新跨平台摸鱼神器&#xff0c;支持小说、股票、网页、视频、直播、PDF、游戏等摸鱼模式&#xff0c;为上班族打造的上班必备神器&#xff0c;使用此软件可以让上班倍感轻松&#xff0c;远…

作者头像 李华
网站建设 2026/2/12 4:32:02

工业环境下screen+抗干扰设计核心要点

屏蔽不止一层&#xff1a;工业现场抗干扰的“screen”实战手册工厂里的PLC突然掉线&#xff0c;传感器读数莫名其妙跳变&#xff0c;HMI触摸屏在电焊机启动时直接失灵……这些看似随机的故障&#xff0c;背后往往藏着同一个元凶——电磁干扰&#xff08;EMI&#xff09;。在变频…

作者头像 李华
网站建设 2026/2/20 13:07:21

图解说明Keil5添加STM32F103芯片库全过程

手把手教你解决Keil5找不到STM32F103芯片的难题你有没有遇到过这样的情况&#xff1a;刚打开Keil Vision5&#xff0c;信心满满地准备新建一个STM32F103C8T6的工程&#xff0c;结果在“Select Device for Target”对话框里输入“STM32F103”&#xff0c;却啥也没搜出来&#xf…

作者头像 李华