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时,我个人习惯先搞清楚三个参数:

  1. 数据宽度:32位还是64位?这决定了带宽上限。
  2. 突发长度:一次连续传输多少拍?我一般设16或32,太大容易占总线太久。
  3. 传输模式:轮询、中断还是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帧缓冲 DDR读写 HDMI输出 数据从左侧流入,经过各级处理,最终输出到显示器

你看这个管道,每一级之间都是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配置搞明白,视频管道自然就通了。

可可精品资料库