4、Aurora用户接口:用户接口信号说明、AXI4-Stream接口详解、流控制与握手机制
好,咱们今天聊聊Aurora协议里最核心、也最需要吃透的部分——用户接口。说白了,你写RTL代码,最终打交道的就是这一堆信号。我刚开始接触Aurora的时候,看着那几十个信号名,说实话有点懵。但后来发现,只要抓住几个关键点,这玩意儿其实挺清晰的。
4.1 用户接口信号概览
Aurora的用户接口,本质上是把你应用层的数据,打包成适合高速串行传输的格式。它主要分两类信号:一类是数据通道,一类是控制通道。
我习惯把用户接口信号分成三组来看:
- 数据信号:就是你要发送和接收的数据,以及对应的字节使能。
- 流控制信号:告诉对方“我准备好了”或者“我满了,你先停一下”。
- 状态与错误信号:比如链路是否建立、有没有CRC错误等。
下面这个表格,是我自己整理的最常用信号,你可以先有个印象:
| 信号名 | 方向 | 说明 |
|---|---|---|
s_axi_tx_tdata |
输入 | 发送数据总线,宽度通常为32或64位 |
s_axi_tx_tvalid |
输入 | 发送数据有效标志 |
s_axi_tx_tready |
输出 | 发送端准备好接收数据 |
s_axi_tx_tlast |
输入 | 指示当前数据是帧的最后一个 |
s_axi_tx_tkeep |
输入 | 字节使能,指示哪些字节有效 |
m_axi_rx_tdata |
输出 | 接收数据总线 |
m_axi_rx_tvalid |
输出 | 接收数据有效标志 |
m_axi_rx_tready |
输入 | 用户逻辑准备好接收数据 |
m_axi_rx_tlast |
输出 | 接收帧结束标志 |
channel_up |
输出 | 通道初始化完成,可以开始传输 |
channel_up 有没有拉高。如果这个信号没起来,后面所有数据交互都是白搭。我曾经有一次折腾了半天,结果发现是参考时钟频率配错了,channel_up 死活不拉高。
4.2 AXI4-Stream接口详解
Aurora的用户接口,采用的是AXI4-Stream协议。你想想看,为什么选它?因为AXI4-Stream简单、高效,特别适合这种点对点的高速数据流。
AXI4-Stream的核心,就是一套握手机制。它只有三个基本信号:
- TVALID:发送方说“我给你的数据是有效的”。
- TREADY:接收方说“我准备好接收了”。
- TDATA:数据总线。
数据传输只发生在 TVALID 和 TREADY 同时为高 的那个时钟周期。这个规则很简单,但实际用起来,有几个坑要注意。
4.2.1 握手机制的三种情况
我画了一张图,帮你理解握手的过程:
你看这个时序图,T1时刻TVALID和TREADY同时为高,数据就传过去了。T2时刻TREADY拉低,说明接收方还没准备好,发送方就得保持数据不变,等着。
4.2.2 TLAST 和 TKEEP 的使用
除了基本握手,还有两个信号在帧传输中特别重要:TLAST 和 TKEEP。
- TLAST:告诉接收方,这是当前帧的最后一个数据。接收方看到TLAST拉高,就知道这一帧结束了,可以开始处理或者转发。
- TKEEP:字节使能。比如你的数据总线是64位(8字节),但最后一拍可能只有3个字节有效。TKEEP就用来指示哪些字节是有效的。
举个例子,你发送一个长度为10字节的帧,数据总线宽度是8字节:
// 第一拍:8字节全部有效
s_axi_tx_tdata = 0x0102030405060708;
s_axi_tx_tkeep = 8'b11111111; // 所有字节有效
s_axi_tx_tlast = 1'b0; // 还没结束
// 第二拍:只有2字节有效
s_axi_tx_tdata = 0x????????????090A; // 高6字节无效
s_axi_tx_tkeep = 8'b00000011; // 只有最低2字节有效
s_axi_tx_tlast = 1'b1; // 帧结束
4.3 流控制与握手机制
流控制,说白了就是解决“发送太快,接收太慢”的问题。Aurora的流控制,主要靠TREADY信号来实现。
4.3.1 背压机制
接收方通过拉低TREADY,给发送方施加“背压”。发送方看到TREADY拉低,就必须暂停发送,直到TREADY再次拉高。
我个人的习惯是,在接收端设计一个FIFO。当FIFO快要满的时候,就拉低TREADY。这样既能保证数据不丢失,又能让发送方知道“我快撑不住了”。
// 伪代码示例:基于FIFO深度的流控制
always @(posedge clk) begin
if (fifo_wr_count > FIFO_THRESHOLD) begin
m_axi_rx_tready <= 1'b0; // FIFO快满了,停止接收
end else begin
m_axi_rx_tready <= 1'b1; // 可以继续接收
end
end
4.3.2 用户流控制(User Flow Control)
Aurora还支持一种叫“用户流控制”的机制。它允许接收方主动发送一个“暂停”请求给发送方。这个机制通过专门的UFC(User Flow Control)接口实现。
不过说实话,我在实际项目中很少用到UFC。大部分场景下,用TREADY背压就足够了。UFC更适合那种需要精确控制发送节奏的场景,比如视频流或者数据采集系统。
4.3.3 避免死锁
流控制设计不好,容易导致死锁。比如,发送方在等接收方拉高TREADY,而接收方又在等发送方发送数据。两边互相等,系统就卡死了。
怎么避免?我总结了两条经验:
- 发送方必须能处理TREADY拉低的情况:不能假设对方永远准备好。设计状态机时,一定要有“等待”状态。
- 接收方不能无限期拉低TREADY:如果接收方因为内部处理不过来而拉低TREADY,必须保证最终能恢复。否则发送方会一直等下去。
嗯,关于用户接口,核心就是这些。你只要把AXI4-Stream的握手规则吃透,再处理好流控制,Aurora的用户接口这块基本就稳了。剩下的,就是在实际项目中多调试、多踩坑,慢慢积累经验。