4、AXI4-Stream协议详解:流式数据传输、TLAST/TKEEP/TUSER信号、数据对齐
AXI4-Stream,说白了就是一条数据管道。没有地址线,没有握手协议里那些复杂的读写通道,就是一股脑地把数据往前推。我刚开始接触这个协议时,觉得它比AXI4-Full简单太多了——确实,上手容易,但想用好,坑也不少。
今天我们就把它彻底讲透。我会结合自己踩过的坑,把TLAST、TKEEP、TUSER这几个信号,以及数据对齐的细节,掰开揉碎了说清楚。
4.1 流式传输的核心思想
AXI4-Stream只有三个通道:发送端(Master)和接收端(Slave)。数据从Master流向Slave,没有回头路。你想想看,这就像一条传送带,零件从一头放上去,另一头取下来。
它的握手信号很简单:
- TVALID:Master告诉Slave,“我这有数据,你准备好接了吗?”
- TREADY:Slave告诉Master,“我准备好了,你发吧。”
- TDATA:数据总线,宽度可以是8、16、32、64、128位等。
握手规则只有一条:TVALID和TREADY同时为高时,数据才被传输。这个规则简单吧?但我在项目中见过有人把TVALID一直拉高,结果Slave还没准备好,数据就丢了。嗯,这里要注意:TVALID一旦拉高,必须保持到握手成功,不能中途撤销。
核心要点:AXI4-Stream没有地址,没有写响应,没有读数据通道。它只关心数据流,不关心数据从哪里来、到哪里去。
4.2 TLAST信号——数据包的边界
TLAST,顾名思义,就是“最后一个”。它告诉接收端:这一笔数据是当前数据包的结尾。为什么需要这个信号?因为流式数据通常是连续不断的,但接收端需要知道什么时候一个包结束了,好去做包边界处理。
举个例子:你从摄像头采集图像,每帧图像有1920×1080个像素。如果每个像素是24位RGB,那么一帧就是1920×1080×3字节。你不可能一次性发完,而是分成多个传输周期。TLAST就在最后一个像素时拉高,告诉接收端:“这帧图像结束了,准备处理下一帧吧。”
我在项目中遇到过一个问题:某个DMA控制器在TLAST到来之前就停止了传输,结果接收端一直等TLAST,导致死锁。后来我加了一个超时机制,才解决这个问题。
避坑指南:我曾经在调试一个视频处理链路时,发现图像总是错位。查了两天才发现,是TLAST信号在包边界处多拉高了一个周期,导致接收端多收了一个无效像素。记住:TLAST必须与最后一个有效数据同时有效,不能提前,也不能延后。
4.3 TKEEP信号——数据对齐的利器
TKEEP,全称是“Byte Keep”。它用来指示TDATA中哪些字节是有效的。为什么需要它?因为数据总线宽度可能大于实际数据宽度。比如你的总线是64位(8字节),但实际数据可能只有3字节。TKEEP就告诉接收端:哪些字节是真实的,哪些是填充的。
TKEEP的位宽等于TDATA的字节数。比如64位总线,TKEEP就是8位。每一位对应一个字节:1表示有效,0表示无效。
| TDATA位宽 | TKEEP位宽 | 说明 |
|---|---|---|
| 8位 | 1位 | 只有一个字节,TKEEP要么是1要么是0 |
| 16位 | 2位 | 两个字节,TKEEP[0]对应低字节,TKEEP[1]对应高字节 |
| 32位 | 4位 | 四个字节,TKEEP[0]对应最低字节 |
| 64位 | 8位 | 八个字节,以此类推 |
你想想看,如果没有TKEEP,接收端怎么知道哪些字节是有效的?它只能假设所有字节都是有效的。但实际应用中,数据往往不是对齐的。比如一个以太网包,长度可能是1518字节,而总线是64位,最后一笔数据可能只有6个字节有效。TKEEP就派上用场了。
个人习惯:我一般会在设计IP时,把TKEEP和TDATA一起传递到下游模块。即使当前模块不需要TKEEP,也建议保留它,因为下游模块可能需要。我曾经因为省掉TKEEP,导致后续调试时花了三天才加上。
4.4 TUSER信号——用户自定义的元数据
TUSER,就是用户自定义的边带信号。它不参与数据传输,但可以携带一些元信息。比如视频流中,可以用TUSER来标记每一行的起始、每一帧的起始。或者在高性能计算中,用TUSER来传递数据包的优先级、时间戳等。
TUSER的位宽是用户自定义的,可以是1位,也可以是128位。但要注意:TUSER必须与对应的TDATA同步传输。也就是说,当TVALID和TREADY握手成功时,TUSER的值必须与TDATA一起被接收端捕获。
我记得有一次,一个同事把TUSER当成全局信号来用,结果在流水线中,TUSER和TDATA错位了。调试时发现,数据包的时间戳全乱了。后来我建议他把TUSER也通过寄存器打拍,和TDATA保持同步,问题就解决了。
常见用途:
- 视频流:TUSER[0]标记帧起始,TUSER[1]标记行起始
- 网络包:TUSER携带包长度、校验和
- DMA传输:TUSER携带数据源地址或目的地址
4.5 数据对齐的实战技巧
数据对齐,说白了就是让数据在总线上“站对位置”。AXI4-Stream没有地址,所以对齐问题完全靠TKEEP和TSTRB(字节选通,但Stream中很少用)来解决。
常见的对齐场景有两种:
- 包起始对齐:数据包的第一个字节必须放在TDATA的最低有效字节位置。这是AXI4-Stream的默认规则,几乎所有IP都遵循这个约定。
- 包内对齐:数据包内部的字节可以不对齐,但TKEEP必须正确指示哪些字节有效。
举个例子:假设总线是32位(4字节),你要传输一个5字节的数据包。那么:
- 第一笔数据:TDATA[31:0]包含字节0~3,TKEEP=4'b1111
- 第二笔数据:TDATA[7:0]包含字节4,TDATA[31:8]是无效的,TKEEP=4'b0001,TLAST=1
你看,第二笔数据中,有效字节在最低位,高位都是填充的。这就是对齐的典型做法。
避坑指南:我曾经在一个PCIe DMA项目中,发现数据总是错位。查了半天,发现是发送端把有效字节放在了TDATA的高位,而接收端默认在低位。后来统一了约定:所有IP都遵循“最低字节优先”原则,问题才解决。
4.6 知识体系图
下面这张图展示了AXI4-Stream协议的核心信号和它们之间的关系。你可以看到,TDATA是主干,TKEEP和TUSER是辅助,TLAST是边界标记。
4.7 实战中的注意事项
最后,我总结几个实战中容易忽略的点:
- TVALID不能依赖TREADY:Master不能等TREADY拉高才拉TVALID,这样会造成死锁。TVALID应该独立拉高,然后等待TREADY。
- TLAST必须与最后一个数据同时有效:不能提前一个周期,也不能延后。否则接收端会误判包边界。
- TKEEP在非对齐传输中必须正确设置:如果TKEEP全为0,接收端应该忽略这笔数据。但有些IP不支持全0的TKEEP,需要查手册。
- TUSER的位宽要提前约定好:不同IP之间对接时,TUSER的位宽和含义必须一致。我见过两个IP的TUSER位宽不同,结果数据错位,调试了两天。
个人建议:在设计AXI4-Stream接口时,尽量把TKEEP和TUSER都保留出来。即使当前模块用不到,后续扩展时也会方便很多。我吃过这个亏,后来所有IP都默认带TKEEP和TUSER,哪怕只接GND。
好了,AXI4-Stream的核心内容就这些。记住:流式传输的核心是数据流,TLAST标记边界,TKEEP处理对齐,TUSER携带元数据。把这几个信号用好,你的流式设计就不会出大问题。