3. AXI-Stream在图像传输中的应用

好,咱们进入第三章。这一章我要跟你聊聊AXI-Stream在图像传输中的具体玩法。说实话,AXI-Stream这个协议本身并不复杂,但一旦跟图像数据结合起来,就有很多门道了。我当年第一次做视频采集卡的时候,就被TUSER和TLAST这两个信号折腾得不轻。

3.1 视频流打包:从像素到数据包

图像数据在硬件里怎么传输?说白了,就是把一个个像素点打包成数据流。你想想看,一张1920x1080的图像,有200多万个像素,总不能一个像素一个像素地单独传吧?

AXI-Stream协议允许我们把多个像素拼成一个数据包。比如一个32位的数据总线,可以同时传输4个8位的像素(灰度图),或者1个32位的RGB像素。我个人习惯把这种打包方式叫做「像素合并」。

关键点:打包的粒度取决于你的数据总线宽度和像素位宽。总线越宽,单周期传输的像素越多,但逻辑复杂度也会上升。

我在项目中遇到过一种情况:摄像头输出的是10位RAW数据,但AXI-Stream总线是64位的。怎么办?一个周期塞6个像素,剩下4位闲置。虽然有点浪费,但换来了更高的吞吐率。

3.2 TUSER信号:行同步的载体

TUSER这个信号,很多初学者容易忽略。但我要说,在图像传输中,TUSER就是你的「行同步信号」。

为什么需要TUSER?因为图像是一行一行扫描的。每一行像素的开始和结束,光靠TLAST是不够的。TLAST只能告诉你一个数据包结束了,但图像帧是由多行组成的,你还需要知道每一行的边界。

我通常这样定义TUSER:

  • TUSER[0]:行起始标志(SOF),每行第一个像素时拉高
  • TUSER[1]:行结束标志(EOL),每行最后一个像素时拉高
  • TUSER[2]:帧起始标志(SOF),每帧第一个像素时拉高

嗯,这里要注意:不同IP核的TUSER定义可能不一样。Xilinx的VDMA和Intel的Streaming Video IP,对TUSER的用法就有细微差别。我曾经因为没仔细看手册,直接把两个IP的TUSER对接,结果图像错位了整整一天才找到原因。

避坑指南:对接不同厂商的IP时,一定要确认TUSER的位宽和每个bit的含义。不要想当然地认为「大家都是这么用的」。

3.3 TLAST信号:帧同步的关键

TLAST是AXI-Stream协议里最常用的控制信号。在图像传输中,TLAST用来标记一帧数据的结束。说白了,就是告诉接收端:「这包数据完了,下一包是新的一帧」。

但TLAST的生成时机很有讲究。以1080p图像为例:

  • 每行有1920个像素
  • 如果每个周期传4个像素,一行需要480个周期
  • TLAST应该在每行的最后一个数据周期拉高
  • 但帧的TLAST,是在最后一行的最后一个数据周期拉高

你可能会问:那TLAST和TUSER怎么配合?我的做法是:

// 伪代码示例
always @(posedge clk) begin
  if (hcount == H_ACTIVE - 1) begin  // 行尾
    tlast <= 1'b1;
    tuser[0] <= 1'b0;  // 行起始清零
    if (vcount == V_ACTIVE - 1) begin  // 帧尾
      tuser[2] <= 1'b1;  // 帧起始标志
    end
  end else begin
    tlast <= 1'b0;
    tuser[0] <= (hcount == 0) ? 1'b1 : 1'b0;
  end
end

个人经验:我建议把TLAST和TUSER的生成逻辑单独封装成一个模块。这样调试的时候,只需要看这几个信号就能定位问题,不用翻整个代码。

3.4 行同步与帧同步的实现

行同步和帧同步,说白了就是告诉接收端:「现在开始传新的一行了」和「现在开始传新的一帧了」。

在硬件实现上,我通常用两个计数器:

  • 行计数器(hcount):从0计数到一行像素数-1
  • 帧计数器(vcount):从0计数到一帧行数-1

这两个计数器配合TUSER和TLAST,就能精确控制视频流的同步。我见过有些工程师只用TLAST做帧同步,结果行同步全靠接收端自己算。这种做法在连续传输时没问题,但一旦出现丢包或错位,整个图像就全乱了。

我个人强烈建议:行同步和帧同步都要用显式信号。TUSER做行同步,TLAST做帧同步,两者缺一不可。

3.5 知识体系总览

下面这张图是我自己总结的AXI-Stream视频传输结构,你看一眼就能明白各个信号的关系:

AXI-Stream视频传输结构图 像素数据 TDATA 视频流打包 像素合并 + 控制信号 同步信号生成 TUSER / TLAST 行同步 TUSER[0] 帧同步 TLAST AXI-Stream 完整视频流 数据流方向:像素数据 → 打包 → 添加同步信号 → 输出AXI-Stream TUSER负责行同步,TLAST负责帧同步,两者配合实现完整视频流

3.6 实际项目中的注意事项

最后,我分享几个实际项目中踩过的坑:

  1. TUSER的位宽问题:不同IP核的TUSER位宽可能不同,有的用1位,有的用4位。对接前一定要确认。
  2. TLAST的时序:TLAST必须在最后一个有效数据时拉高,不能早也不能晚。早了会丢数据,晚了会多数据。
  3. 行同步的连续性:如果一行数据被分成了多个AXI-Stream包,TUSER只在第一个包有效。这个细节很容易被忽略。
  4. 帧间隔处理:两帧之间要有足够的空闲周期,给接收端处理时间。我一般留4-8个时钟周期的间隔。

总结一下:AXI-Stream在图像传输中的核心,就是用好TUSER和TLAST这两个信号。TUSER管行同步,TLAST管帧同步。打包的时候注意像素合并的粒度,同步的时候注意信号的时序配合。做到这几点,视频流传输基本不会出大问题。