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视频传输结构,你看一眼就能明白各个信号的关系:
3.6 实际项目中的注意事项
最后,我分享几个实际项目中踩过的坑:
- TUSER的位宽问题:不同IP核的TUSER位宽可能不同,有的用1位,有的用4位。对接前一定要确认。
- TLAST的时序:TLAST必须在最后一个有效数据时拉高,不能早也不能晚。早了会丢数据,晚了会多数据。
- 行同步的连续性:如果一行数据被分成了多个AXI-Stream包,TUSER只在第一个包有效。这个细节很容易被忽略。
- 帧间隔处理:两帧之间要有足够的空闲周期,给接收端处理时间。我一般留4-8个时钟周期的间隔。
总结一下:AXI-Stream在图像传输中的核心,就是用好TUSER和TLAST这两个信号。TUSER管行同步,TLAST管帧同步。打包的时候注意像素合并的粒度,同步的时候注意信号的时序配合。做到这几点,视频流传输基本不会出大问题。