news 2026/2/27 13:13:16

AXI DMA入门全攻略:软硬件协同设计初体验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AXI DMA入门全攻略:软硬件协同设计初体验

AXI DMA实战指南:从零搭建高效软硬件数据通路

你有没有遇到过这样的场景?
FPGA采集的图像帧速率越来越高,但CPU却忙于搬运像素数据,几乎没法做任何实际处理。或者你的软件无线电接收机在高采样率下频频丢包——不是因为逻辑出错,而是数据搬不动了

这正是我们今天要解决的核心问题:如何让数据“自己跑”起来,而不是靠CPU一五一十地去搬?

答案就是——AXI DMA(Advanced eXtensible Interface Direct Memory Access)。它不是一个神秘黑盒,而是一套精心设计的软硬件协同机制,能把原本压垮CPU的数据洪流,变成安静流淌在PL与PS之间的高速通道。

接下来,我将以一个真实开发者的视角,带你一步步拆解AXI DMA的工作原理、关键配置和实战技巧,不讲空话,只讲你能用上的东西。


为什么我们需要AXI DMA?

先来看一组对比:

场景CPU轮询搬运使用AXI DMA
1080p视频采集(~373MB/s)CPU占用 >95%,系统卡死CPU占用 <10%,轻松处理AI推理
SDR基带信号接收(100Msps IQ)数据丢失严重实时稳定接收
工业传感器多通道采样帧间隔抖动大,难以同步固定延迟,精确到微秒级

根本原因在于:传统方式中,每一个字节都要经过CPU的手。无论是读取外设寄存器还是写入内存,都会触发总线访问、缓存操作甚至上下文切换。而AXI DMA的本质,是把数据搬运这件事彻底交给硬件去做

它的核心价值可以用三个词概括:
-卸载(Offload):解放CPU,让它专注业务逻辑。
-提速(Throughput):利用AXI4突发传输,逼近DDR理论带宽极限。
-确定性(Determinism):固定延迟,适合实时系统。

那么,它是怎么做到的?我们从最基础的结构说起。


AXI DMA到底是什么?两个通道,一张图说清

打开Xilinx Vivado IP Catalog,搜索AXI DMA,你会看到这个IP核有两个主要接口:

  • MM2S(Memory Map to Stream):从内存读数据,发给PL侧外设(比如DAC或Ethernet MAC)
  • S2MM(Stream to Memory Map):从PL侧输入流(如ADC或摄像头),写入DDR内存

💡 简单记法:“M”代表Memory,“S”代表Stream;字母顺序就是方向。MM2S = 内存→流,S2MM = 流→内存。

这两个通道完全独立,可以同时工作,实现全双工通信。每个通道内部又包含三大部分:

[CPU控制] ← AXI Lite → [DMA控制器] ↘ → [AXI Master接口] ↔ DDR控制器(HP端口) ↗ [数据流] ← AXI Stream → [FIFO & 描述符引擎]

其中最关键的,是那个叫“描述符”的东西。


不是简单搬运工:AXI DMA靠“任务清单”工作

很多人以为DMA就是“告诉它起点终点长度,然后GO”。其实现代DMA更像是一个智能快递调度中心,它靠一份“任务清单”来执行批量操作。

这份清单叫做Descriptor(描述符),每条记录包含:

字段含义
Buffer Address数据要送到哪块物理内存
Transfer Length搬多少字节
Control Flags是否最后一帧?是否触发中断?
Next Descriptor Pointer下一个任务在哪(可选)

当你启用Scatter-Gather模式时,这些描述符会被连成一个环形队列,DMA自动按序执行,直到你喊停。

这就引出了一个关键优势:CPU只需初始化一次,后续传输全自动进行。哪怕内存区域不连续也没关系——这就是所谓的“分散-聚集”(Scatter-Gather)能力。

举个例子:你想采集100帧图像,每帧6MB。如果不用SG模式,你得中断100次,每次重新配置DMA;用了之后呢?填好100个描述符,启动一次,剩下的交给硬件。


关键参数怎么配?别再瞎猜了

在Vivado里配置AXI DMA IP时,有几个参数直接影响性能和稳定性,必须根据应用场景合理设置:

参数推荐值说明
Data Width32 或 64 bit匹配你的数据路径宽度。如果是RGB像素流,通常32位够用;高速ADC建议64位
Burst Size16~32 beats控制每次AXI突发传输的数据量。太大可能阻塞其他主设备,太小则效率低。经验法则是:Burst × DataWidth ≈ 512~1024 bytes
Include Scatter Gather✔️ 开启只要涉及多缓冲或多帧传输,就必须开!否则只能传单块数据
Address Width32 位足够Zynq-7000/MPSoC一般有2GB以上地址空间,32位寻址可达4GB,绰绰有余
Maximum Transfer Length≥ 单帧大小设置为最大可能的一次传输长度,避免溢出错误

⚠️ 特别提醒:如果你关闭Scatter-Gather功能,MM2S/S2MM通道将退化为“简单直通”模式,无法支持多缓冲自动切换!


中断怎么接?别让信号“迷路”

DMA做完事了,怎么通知CPU?靠中断。

但在Zynq系统中,PL侧产生的中断不能直接接到ARM核上,必须通过一个中间桥梁——AXI Interrupt Controller(简称INTC)。

你可以把它想象成一个“小区门卫”:多个住户(外设)有事要找物业(CPU),都先告诉门卫,由他统一上报。

典型连接如下:

[AXI DMA] → TC_IRQ (传输完成) ↓ [AXI INTC] → IRQ_F2P[0] → GIC → CPU ↑ [Timer, UART...] 其他中断源

代码层面也很清晰:

#include "xintc.h" #include "xaxidma.h" static XIntc Intc; static XAxiDma AxiDma; void dma_isr(void *Callback) { u32 irq_status; // 读取DMA状态寄存器 irq_status = XAxiDma_IntrGetIrq(&AxiDma, XAXIDMA_S2MM_DIR); // 清除已完成中断标志 XAxiDma_IntrAckIrq(&AxiDma, irq_status, XAXIDMA_S2MM_DIR); // 检查是否为帧完成中断 if (irq_status & XAXIDMA_IRQ_IOC_MASK) { transfer_done = true; // 标志置位,唤醒主循环 } // 处理错误(强烈建议加入!) if (irq_status & (XAXIDMA_IRQ_ERROR_MASK)) { xil_printf("DMA Error detected!\r\n"); // 这里应包含复位或恢复逻辑 } }

注册流程三步走:
1. 初始化INTC并启动
2. 将DMA中断ID绑定到ISR函数
3. 使能全局异常和中断

XIntc_Enable(&Intc, XPAR_AXIDMA_0_S2MM_INTROUT_VEC_ID); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XIntc_InterruptHandler, &Intc); Xil_ExceptionEnable();

✅ 小贴士:即使你在Linux环境下使用UIO驱动,底层依然依赖这套机制。裸机开发时更要亲手写一遍,才能真正理解中断流向。


Scatter-Gather真香警告:从此告别频繁中断

让我问你一个问题:
如果你想持续采集视频流,是希望每帧中断一次,还是每100帧才中断一次?

当然是后者!但现实往往是前者——因为你没开启Scatter-Gather。

SG模式下,DMA会自动在一个描述符队列中循环传输。例如你准备了4个缓冲区:

Buffer[0] ← Frame 0 → Done → Move to next Buffer[1] ← Frame 1 → Done → Move to next Buffer[2] ← Frame 2 → Done → Move to next Buffer[3] ← Frame 3 → Done → Wrap to Buffer[0]

整个过程无需CPU干预,只有当你想获取某帧数据时,才去检查“当前已写完哪一帧”。

Xilinx库提供了简洁API:

// 配置SG模式下的传输队列 for (int i = 0; i < NUM_BUFFERS; i++) { ConfigPtr->WriteChannel.SGList[i].physAddr = (UINTPTR)tx_buffer[i]; ConfigPtr->WriteChannel.SGList[i].len = FRAME_SIZE; ConfigPtr->WriteChannel.SGList[i].control = XVidC_VideoFrameStoreControlFlags(0, 0, 1); // EOF=1 } // 启动循环传输 XAxiDma_StartVideo(&AxiDma, FRAME_SIZE, NUM_BUFFERS);

效果立竿见影:
- 中断频率下降75%(四缓冲为例)
- CPU调度更平稳,更适合运行操作系统
- 数据到达时间更加确定,减少抖动


实战案例:摄像头图像采集系统怎么做?

设想你要做一个基于CMOS传感器的视觉终端,要求:1080p@30fps,RGB888格式,后续要做边缘检测。

系统架构长这样:

[OV5640 Sensor] ↓ LVDS [FPGA PL Logic] → [AXI4-Stream Video] ↓ [AXI DMA (S2MM)] ↓ [DDR3 内存] ↑ [ARM Cortex-A9] (OpenCV + GUI)

步骤分解:

  1. 硬件搭建(Vivado Block Design)
    - 添加AXI DMA IP,勾选S2MM通道 + Scatter Gather
    - 连接M_AXIS_MM2S到视频桥接模块
    - M_AXI_MM2S接口走HP0端口接入DDR
    - S_AXI_LITE接Lite总线用于CPU配置
    - 中断输出接AXI INTC

  2. 内存分配(关键!)
    ```c
    #define FRAME_SIZE (1920 * 1080 * 3) // RGB888
    #define NUM_BUFFERS 4

u8 *buffers[NUM_BUFFERS];
UINTPTR phy_addr[NUM_BUFFERS];

for (int i = 0; i < NUM_BUFFERS; i++) {
buffers[i] = (u8*)Xil_Memalign(0x1000, FRAME_SIZE); // 对齐4KB页
phy_addr[i] = XXil_VirtToPhys(buffers[i]); // 获取物理地址
Xil_DCacheInvalidateRange((UINTPTR)buffers[i], FRAME_SIZE); // 初始无效化
}
```

  1. 启动DMA接收
    c // 提交所有缓冲区到SG队列 for (int i = 0; i < NUM_BUFFERS; i++) { XAxiDma_SimpleTransfer(&AxiDma, phy_addr[i], FRAME_SIZE, XAXIDMA_DEVICE_TO_DMA); }

  2. 中断处理
    ```c
    void dma_isr(void *Callback) {
    static int completed_idx = 0;

    XAxiDma_IntrAckIrq(&AxiDma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DEVICE_TO_DMA);

    // 当前完成的是第几个buffer?
    int current_done = completed_idx % NUM_BUFFERS;
    process_image_frame(buffers[current_done]); // 启动处理任务

    // 提交同一个buffer用于下一帧(循环使用)
    XAxiDma_SimpleTransfer(&AxiDma, phy_addr[current_done], FRAME_SIZE, XAXIDMA_DEVICE_TO_DMA);

    completed_idx++;
    }
    ```

  3. 缓存一致性管理

    ⚠️ 最容易踩坑的地方!

PL写DDR → CPU读图像 → 必须确保数据已从缓存刷新!

  • 裸机环境:Xil_DCacheInvalidateRange((UINTPTR)buf, len);在读之前调用
  • Linux环境:使用dma_alloc_coherent()分配一致性内存,自动处理

常见坑点与调试秘籍

❌ 坑1:明明写了地址,怎么数据没进来?

检查以下几点:
- 物理地址是否正确?Xil_VirtToPhys()不能少
- 缓存是否未失效?导致CPU读到了旧数据
- HP端口是否使能?在Zynq Processing System配置中确认M_AXI_HP0已启用
- 时钟是否稳定?PL与PS时钟域交叉需加同步FIFO

❌ 坑2:传输一会儿就卡住,状态寄存器显示Idle

大概率是描述符耗尽!尤其是在SG模式下,如果没有及时重新提交buffer,DMA会在最后一项完成后停止。

解决方案:在ISR中立即重新提交已完成的buffer。

❌ 坑3:带宽上不去,实测只有300MB/s?

看看这几个优化点:
- Burst Size是否足够大?至少16-beat起步
- 数据位宽是否匹配?64位比32位吞吐高近一倍
- 是否开启了乱序传输?在高级配置中允许Out-of-Order Completion可提升效率
- DDR是否处于高性能模式?避免与其他高负载模块争抢带宽

✅ 秘籍:快速验证DMA通路的方法

不想写一堆代码?试试这个快捷方法:

  1. 在SDK中创建裸机工程
  2. 调用XAxiDma_SimpleTransfer()发起一次回环测试
  3. 用ILA抓AXI信号,观察是否有READ/WRITES事务
  4. 或者直接打印DDR中预期位置的数据变化

只要能看到数据流动,说明通路打通了一半。


总结一下:AXI DMA不是功能,是一种思维方式

掌握AXI DMA,不只是学会调用几个API那么简单。它背后体现的是现代嵌入式系统的一种核心设计哲学:

让合适的模块干合适的事

  • CPU擅长决策、调度、协议处理;
  • FPGA擅长并行计算、高速接口;
  • DMA则专精于一件事:高效、可靠、自动化地移动数据

当你能把这三者有机结合起来,你会发现,原来那些看似瓶颈的问题——带宽不够、实时性差、CPU跑满——都可以迎刃而解。

未来随着Versal、Kria等新型异构平台普及,DMA的角色只会越来越重。它不再只是“辅助搬运”,而是AI Engine、NoC、PL之间数据调度的核心枢纽

所以,别再说“我会用FPGA就行”了。真正的高手,懂得如何让数据自己跑起来。

如果你正在做图像、通信或工业控制项目,不妨现在就打开Vivado,试着加一个AXI DMA进去。也许下一帧就不会丢了。

有问题欢迎留言讨论,我可以分享具体的Tcl脚本或Block Design模板。一起把数据流跑起来!

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

好写作AI:工具or代笔?如何正当提升学术生产力?

当你向同学推荐好写作AI时&#xff0c;是否曾在对方眼中捕捉到一丝微妙的质疑&#xff1a;“这……和找代写有什么区别&#xff1f;” 恭喜你&#xff0c;你正站在一场认知革命的前线。深夜的实验室&#xff0c;小陈正熟练地使用好写作AI整理实验数据。对面桌的师兄投来复杂的目…

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

STM32与外部传感器通信中的奇偶校验应用

让你的STM32串口通信不再“玄学”&#xff1a;奇偶校验实战全解析 你有没有遇到过这样的情况&#xff1f; 系统运行得好好的&#xff0c;突然某个温湿度传感器上报了一个 负200℃ 的温度值&#xff1b; 或者压力读数莫名其妙跳到几百kPa&#xff0c;重启后又恢复正常&#…

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

51单片机控制有源/无源蜂鸣器唱歌的频率差异解析

51单片机如何让蜂鸣器“唱歌”&#xff1f;有源与无源的本质差异全解析你有没有在某个项目里&#xff0c;明明代码写得一丝不苟&#xff0c;蜂鸣器却只发出一声“嘀”&#xff0c;死活唱不出《小星星》&#xff1f;或者更离谱——你给它送了一串频率变化的信号&#xff0c;结果…

作者头像 李华
网站建设 2026/2/24 18:29:29

8个基本门电路图实战案例:从搭建到验证全过程

从零搭建8种基本门电路&#xff1a;一次彻底的数字电路实战之旅你有没有过这样的经历&#xff1f;明明理论学得滚瓜烂熟&#xff0c;可一旦拿起面包板、跳线和芯片&#xff0c;却发现输出信号“莫名其妙”——LED该亮不亮&#xff0c;示波器上全是毛刺&#xff0c;逻辑状态对不…

作者头像 李华
网站建设 2026/2/27 6:01:43

Qwen3-VL智能客服搭建:1块钱测试对话+视觉能力

Qwen3-VL智能客服搭建&#xff1a;1块钱测试对话视觉能力 1. 为什么选择Qwen3-VL做智能客服&#xff1f; 电商店主们最头疼的客服问题是什么&#xff1f;就是那些"图片里这件衣服有没有蓝色M码&#xff1f;"、"我发的产品截图和描述不符怎么办&#xff1f;&qu…

作者头像 李华
网站建设 2026/2/25 3:27:09

AutoGLM-Phone-9B开发手册:企业级AI应用快速落地方案

AutoGLM-Phone-9B开发手册&#xff1a;企业级AI应用快速落地方案 随着移动智能设备在企业服务、现场作业、远程协作等场景中的广泛应用&#xff0c;对本地化、低延迟、高安全性的AI推理能力需求日益增长。传统云端大模型虽具备强大语义理解能力&#xff0c;但受限于网络延迟、…

作者头像 李华