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的“舞蹈”。有三种情况:

  1. VALID先有效,READY后有效:发送方先准备好数据,等接收方准备好后传输。
  2. READY先有效,VALID后有效:接收方先表示可以收,发送方准备好后传输。
  3. 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互联的结构:

AXI总线互联结构图 AXI总线互联结构 PS (Master) PL Master AXI Interconnect 地址译码 数据路由 时钟域转换 Slave 1 (DDR) Slave 2 (BRAM) Slave 3 (Reg) 说明: AXI Interconnect负责将多个Master的请求路由到正确的Slave,支持地址译码和时钟域转换。

桥接,说白了就是协议转换。比如你有一个自定义的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 实战中的避坑指南

最后,我总结几个实战中容易踩的坑:

  1. 地址对齐:AXI4-Lite和AXI4-Full的地址必须对齐到数据宽度。32位总线,地址必须是4的倍数。
  2. 突发边界:INCR突发不能跨越4KB边界。这是AXI协议的硬性规定,因为地址译码是以4KB为单位的。
  3. 响应超时:如果Slave不返回响应,Master会一直等。我建议在设计中加一个超时计数器,防止死锁。
  4. 时钟域转换:PS和PL可能在不同时钟域。AXI Interconnect可以处理异步时钟,但要注意设置正确的时钟频率。

嗯,AXI协议的内容就讲到这里。记住一句话:Stream传数据,Lite配寄存器,Full搬大块。选对协议,你的设计就成功了一半。