4、AXI4-Stream接口实战:高速数据流传输、DMA控制器配置、视频流处理管道搭建
AXI4-Stream,说白了就是FPGA世界里最常用的“数据水管”。
它不像AXI4-Full那样有地址概念,也不像AXI4-Lite那样慢吞吞。它就是一股脑地往前推数据,一拍一拍地流过去。我刚开始接触Zynq时,总觉得这玩意儿太简单——不就是个valid和ready握手嘛。后来做视频项目才发现,这里面的坑,一个比一个深。
4.1 AXI4-Stream协议核心:握手机制与数据对齐
先看最基本的握手信号。一个AXI4-Stream接口,最少只需要三根线:
- TVALID:发送方说“我这拍数据有效”
- TREADY:接收方说“我准备好收了”
- TDATA:数据总线,宽度通常是8、16、32或64位
数据传输只在TVALID和TREADY同时为高的时钟沿发生。这个规则,你想想看,是不是很像两个人递东西——一个伸手,一个递,同时发生才算交接成功。
核心要点:AXI4-Stream没有地址线,数据是顺序流动的。谁先来谁先走,不能跳着读。
除了这三根,还有几个辅助信号:
- TKEEP:指示哪些字节是有效数据。比如64位总线只传3个字节,TKEEP就告诉你哪3个字节是真的。
- TLAST:包结束标志。一个数据包的最后一次传输,TLAST拉高。
- TUSER:用户自定义信号。我经常用它来传帧同步信号或行同步信号。
- TID/TDEST:流标识符,多路复用时用。
我在项目中遇到过一个问题:DMA读回来的图像数据总是错位。查了两天,最后发现是TKEEP没处理好。接收端以为每个字节都有效,实际上发送端只填了部分字节。嗯,从那以后我再也不敢忽略TKEEP了。
4.2 DMA控制器配置:从轮询到中断再到Scatter-Gather
DMA(直接存储器访问)是高速数据流的核心。没有DMA,CPU得一个一个字节搬数据,那效率简直没法看。
Zynq里常用的DMA控制器有两种:
- AXI DMA:轻量级,适合简单场景
- AXI VDMA:带帧缓冲管理,专为视频设计
配置DMA时,我个人习惯先搞清楚三个参数:
- 数据宽度:32位还是64位?这决定了带宽上限。
- 突发长度:一次连续传输多少拍?我一般设16或32,太大容易占总线太久。
- 传输模式:轮询、中断还是Scatter-Gather?
小技巧:Scatter-Gather模式允许DMA自动处理不连续的内存块。你只需要维护一个描述符链表,DMA自己会一个一个执行。这在处理视频帧时特别有用——每帧数据可能分散在不同的内存地址。
配置代码示例(以AXI DMA为例):
// 初始化DMA
XDma_Config *Config;
XDma DmaInstance;
Config = XDma_LookupConfig(XPAR_AXI_DMA_0_DEVICE_ID);
XDma_CfgInitialize(&DmaInstance, Config);
// 配置传输描述符
XDma_Bd BdTemplate;
XDma_BdClear(&BdTemplate);
XDma_BdSetBufAddr(&BdTemplate, (UINTPTR)BufferAddr);
XDma_BdSetLength(&BdTemplate, TransferLength, &DmaInstance);
XDma_BdSetCtrl(&BdTemplate, XDMA_BD_CTRL_TXSOF_MASK | XDMA_BD_CTRL_TXEOF_MASK);
// 启动传输
XDma_Start(&DmaInstance, XDMA_CHANNEL_S2MM);
XDma_BdRingStart(&DmaInstance.TxBdRing);
我曾经踩过一个坑:DMA中断没配置好,导致传输完成信号丢失。数据明明已经搬完了,CPU却一直等。后来我在中断服务函数里加了超时处理,才算彻底解决。
警告:DMA传输过程中,千万不要让CPU去读写正在被DMA操作的内存区域。否则会出现数据一致性问题。解决办法是用双缓冲(ping-pong buffer)——DMA写一块,CPU读另一块,交替使用。
4.3 视频流处理管道:从摄像头到显示器的完整链路
视频流处理,说白了就是一条流水线:采集→处理→显示。每一级都用AXI4-Stream连接。
典型的视频管道长这样:
- 采集端:摄像头通过MIPI或并行接口进入FPGA
- 格式转换:把原始Bayer格式转成RGB或YUV
- 图像处理:缩放、色彩校正、边缘增强等
- 帧缓冲:通过VDMA写入DDR,再读出来
- 显示输出:HDMI或DisplayPort驱动
我画了一张流程图,帮你理清这个管道:
你看这个管道,每一级之间都是AXI4-Stream连接。好处是:
- 模块化:每一级可以独立开发和测试
- 流水线:各级可以并行工作,延迟只取决于最慢的一级
- 可扩展:想加个滤镜?在中间插一级就行
配置VDMA时,我建议重点关注这几个寄存器:
| 寄存器名称 | 功能描述 | 配置建议 |
|---|---|---|
| VDMA_CR | 控制寄存器,启动/停止 | 先配好其他寄存器,最后写这个 |
| VDMA_FRMDLY_STRIDE | 帧延迟和行跨度 | 跨度要等于图像宽度×像素字节数 |
| VDMA_HSIZE | 水平尺寸(每行字节数) | 必须是突发长度的整数倍 |
| VDMA_VSIZE | 垂直尺寸(行数) | 就是图像高度 |
经验之谈:调试视频管道时,我习惯先让VDMA工作在直通模式(genlock mode),不经过DDR。这样能快速验证前端采集和后端显示是否正常。等直通没问题了,再打开帧缓冲。分步调试,问题定位快得多。
4.4 实战中的避坑指南
做AXI4-Stream项目,有几个坑我反复踩过,今天一并告诉你:
- 背压处理:如果接收方来不及处理,TREADY拉低,发送方必须停等。我见过有人没处理背压,导致数据丢失。
- 时钟域交叉:不同模块可能用不同时钟。跨时钟域时一定要用异步FIFO或握手同步器。
- 带宽计算:AXI4-Stream的理论带宽是时钟频率×数据宽度。但实际要考虑握手开销和突发效率。我一般按70%利用率估算。
- TLAST位置:包结束信号必须精确对齐到最后一个有效数据。偏移一拍,整个数据流就乱了。
我曾经... 在一个4K视频项目中,因为VDMA的帧缓冲地址没对齐到64字节边界,导致DMA传输时断时续。查了整整一天,最后发现是地址对齐问题。从那以后,我所有DMA缓冲区都强制对齐到cache line大小。
好了,这一章的内容就这些。AXI4-Stream接口说难不难,说简单也不简单。关键是把握手机制吃透,把DMA配置搞明白,视频管道自然就通了。
可可精品资料库