4、AXI总线协议:AXI4-Stream、AXI4-Lite、AXI4-Full协议详解
AXI总线,说白了就是ARM和FPGA之间沟通的“语言”。在Zynq平台上,PS(处理系统)和PL(可编程逻辑)之间的数据交换全靠它。我刚开始接触Zynq时,总觉得AXI协议很复杂,各种通道、握手信号看得眼花缭乱。但实际用多了就会发现,它其实就三种变体,每种都有自己的脾气。
嗯,我们先从整体上看看这三种协议的区别。
| 协议类型 | 主要用途 | 数据通道 | 地址/数据关系 | 典型场景 |
|---|---|---|---|---|
| AXI4-Stream | 高速数据流 | 仅数据通道 | 无地址,连续流 | DMA、视频流、FFT数据 |
| AXI4-Lite | 寄存器读写 | 读地址、读数据、写地址、写数据 | 单次传输,非突发 | 配置寄存器、状态查询 |
| AXI4-Full | 高性能内存访问 | 读地址、读数据、写地址、写数据、写响应 | 支持突发传输 | DDR读写、大数据块搬运 |
4.1 AXI4-Stream:纯粹的数据管道
AXI4-Stream是我用得最多的协议。它没有地址线,只有数据线,说白了就是一根纯粹的数据管道。你想想看,做视频处理或者FFT加速时,数据是一拍一拍流进来的,根本不需要地址,只需要告诉对方“数据来了”就行。
它的核心握手信号就三个:
- TVALID:发送方说“我这拍数据有效”。
- TREADY:接收方说“我准备好收了”。
- TDATA:数据总线,宽度可以自定义。
握手规则很简单:TVALID和TREADY同时为高时,数据才被传输。我见过不少新手在这里翻车——只拉了TVALID,没等TREADY就换数据了,结果丢包。
关键点:AXI4-Stream没有反压保护机制。如果接收方来不及处理,你必须自己实现流控。我习惯在Stream路径上加一个FIFO,用Almost Full信号做反压。
另外,Stream协议还支持TLAST、TKEEP、TUSER等辅助信号。TLAST标记帧尾,在包传输时特别有用。TKEEP用于指示哪些字节是有效的,在数据对齐时很关键。
// 一个简单的AXI4-Stream发送逻辑
always @(posedge clk) begin
if (rst) begin
tvalid <= 1'b0;
tdata <= 'd0;
end else begin
if (tvalid && tready) begin
// 握手成功,准备下一拍数据
tvalid <= 1'b0;
tdata <= next_data;
end else if (data_ready) begin
// 有数据要发送
tvalid <= 1'b1;
tdata <= current_data;
end
end
end
4.2 AXI4-Lite:轻量级寄存器访问
AXI4-Lite,名字里带个“Lite”,确实很轻量。它不支持突发传输,一次只能读写一个地址。但它的好处是逻辑简单,占用的资源少。我在项目中通常用它来做控制通路——比如配置加速器的参数、查询状态寄存器。
AXI4-Lite有五个独立通道:
- 写地址通道(AW):携带写地址和写保护信息。
- 写数据通道(W):携带要写入的数据。
- 写响应通道(B):返回写操作的状态(OKAY、EXOKAY、SLVERR、DECERR)。
- 读地址通道(AR):携带读地址。
- 读数据通道(R):返回读出的数据和状态。
每个通道都有自己的VALID和READY握手信号。写操作需要AW和W都握手后,B通道才会返回响应。读操作则是AR握手后,R通道返回数据。
个人经验:AXI4-Lite的地址对齐要求很严格。地址必须是数据宽度的整数倍。我曾经因为地址没对齐,读回来的数据全是乱的,查了半天才发现是地址写错了。
4.3 AXI4-Full:高性能的突发传输
AXI4-Full是完整版的AXI协议,支持突发传输。什么叫突发?就是一次地址请求,后面连续传多拍数据。这对DDR读写来说太重要了——你想想看,如果每次读一个32位数据都要发一次地址,那效率得多低?
AXI4-Full在AXI4-Lite的基础上增加了突发相关的信号:
- AWLEN/ARLEN:突发长度,表示一次突发传多少拍数据。
- AWSIZE/ARSIZE:每拍数据的字节数。
- AWBURST/ARBURST:突发类型(FIXED、INCR、WRAP)。
突发类型有三种:
| 突发类型 | 说明 | 典型场景 |
|---|---|---|
| FIXED | 地址固定不变 | FIFO操作、重复写同一个寄存器 |
| INCR | 地址递增 | 顺序内存访问、数组遍历 |
| WRAP | 地址递增到边界后回绕 | Cache Line填充、环形缓冲区 |
我个人最常用的是INCR模式。做图像处理时,数据在DDR里是连续存放的,用INCR突发读效率最高。WRAP模式在Cache设计中很常见,但FPGA里用得少。
避坑指南:我曾经在AXI4-Full的突发长度上吃过亏。Zynq的AXI互联结构对突发长度有限制,最大支持256拍。但如果你连到DDR控制器,实际支持的突发长度可能更小。建议查一下具体IP核的手册,别想当然。
4.4 握手信号与时序
AXI协议的握手信号,说白了就是VALID和READY的“舞蹈”。有三种情况:
- VALID先有效,READY后有效:发送方先准备好数据,等接收方准备好后传输。
- READY先有效,VALID后有效:接收方先表示可以收,发送方准备好后传输。
- VALID和READY同时有效:一拍完成传输,效率最高。
这里有个重要规则:VALID一旦拉高,就不能依赖READY来撤销。也就是说,发送方拉高VALID后,必须保持数据稳定,直到握手成功。我见过有人想用VALID的下降沿来取消传输,结果违反了协议规范。
// 握手成功的条件
assign handshake = tvalid && tready;
// 数据在握手成功时被采样
always @(posedge clk) begin
if (handshake) begin
captured_data <= tdata;
end
end
4.5 总线互联与桥接
在Zynq平台上,PS和PL之间通过AXI互联结构连接。这个互联结构可以是一个AXI Interconnect IP核,也可以是多个AXI SmartConnect IP核。它的作用是把多个Master和多个Slave连接起来,处理地址译码、数据路由、时钟域转换等。
我画了一张图,帮你理解AXI互联的结构:
桥接,说白了就是协议转换。比如你有一个自定义的Stream接口,想接到AXI4-Lite总线上,就需要一个桥接逻辑。Xilinx提供了很多现成的桥接IP:
- AXI4-Stream to AXI4-Lite:把Stream数据流转换成寄存器读写操作。
- AXI4-Stream to AXI4-Full:把Stream数据流转换成突发读写,常用于DMA。
- AXI4-Lite to AXI4-Full:把单次读写转换成突发,用得少。
我的建议:除非性能要求极高,否则尽量用Xilinx提供的桥接IP。自己写桥接逻辑很容易出时序问题。我见过一个项目,工程师自己写了AXI4-Stream到AXI4-Full的桥接,结果握手逻辑有bug,数据经常丢包,最后还是换成了Xilinx的DMA IP。
4.6 实战中的避坑指南
最后,我总结几个实战中容易踩的坑:
- 地址对齐:AXI4-Lite和AXI4-Full的地址必须对齐到数据宽度。32位总线,地址必须是4的倍数。
- 突发边界:INCR突发不能跨越4KB边界。这是AXI协议的硬性规定,因为地址译码是以4KB为单位的。
- 响应超时:如果Slave不返回响应,Master会一直等。我建议在设计中加一个超时计数器,防止死锁。
- 时钟域转换:PS和PL可能在不同时钟域。AXI Interconnect可以处理异步时钟,但要注意设置正确的时钟频率。
嗯,AXI协议的内容就讲到这里。记住一句话:Stream传数据,Lite配寄存器,Full搬大块。选对协议,你的设计就成功了一半。