从近似0基础开始FPGA开发 -- part.11 AXI总线协议与工程应用

本文承接前面的FPGA逻辑设计与UDP通信系列,系统介绍Vivado工程中最常见的三类AXI接口:AXI4-Stream、AXI4-Lite和AXI4 Memory-Mapped。其中,工程师经常将完整的AXI4 Memory-Mapped接口简称为"AXI Full",本文也沿用这一叫法。

前言

随着FPGA工程规模增加,模块之间不可能一直使用各自定义的datavalidbusydone信号连接。

如果每个模块的接口都不相同,会出现以下问题:

  • 同一个FIFO需要为不同模块重复编写适配逻辑;
  • 数据处理模块无法直接连接DMA、Ethernet或PCIe IP;
  • MicroBlaze访问自定义寄存器时需要单独设计总线;
  • DDR读写接口与普通数据流接口混杂,难以维护;
  • 模块虽然单独仿真正确,集成后却频繁出现丢数和死锁。

AXI的作用,就是为模块之间的数据传输建立统一规则。

在一个较完整的FPGA系统中,三类AXI接口通常同时存在:

text 复制代码
                       FPGA内部典型数据流

传感器/ADC/UDP RX ──► AXI4-Stream ──► DSP/FIFO/DMA
                                             │
                                             ▼
                                     AXI4 Memory-Mapped
                                             │
                                             ▼
                                           DDR4

MicroBlaze/JTAG ─────► AXI4-Lite ─────► 控制寄存器/状态寄存器

三者名字相似,但解决的问题并不相同:

  • AXI4-Stream解决连续数据流的传输;
  • AXI4-Lite解决少量控制寄存器的读写;
  • **AXI4 Memory-Mapped(AXI Full)**解决高吞吐存储器映射访问。

本文先介绍三类接口的共同规则,再分别展开其信号、时序和应用场景,最后给出一个以AXI Full突发读写DDR4为核心的工程架构。


一、AXI协议是什么

AXI全称为Advanced eXtensible Interface,是Arm AMBA总线体系中的一种接口协议。它定义了Initiator和Target之间怎样传递地址、数据与响应,但不规定模块内部必须如何实现。

在较旧的资料和Vivado IP中,经常看到Master/Slave的叫法;新版规范更推荐Initiator/Target。为了方便对应Vivado端口命名,本文保留以下对应关系:

旧称 新称 作用
Master Initiator 发起读写请求的一方
Slave Target 接收请求并返回数据或响应的一方

例如:

text 复制代码
MicroBlaze访问GPIO寄存器:MicroBlaze是Initiator,AXI GPIO是Target
DMA向DDR写入数据:       DMA是Initiator,MIG DDR控制器是Target
FFT向DMA输出数据流:      FFT是Stream Source,DMA是Stream Sink

1.1 AXI不是某一根物理总线

AXI描述的是FPGA内部模块的逻辑接口,不是像RGMII、SPI那样直接对应板外引脚。

text 复制代码
AXI4-Stream   ── FPGA内部数据流接口
AXI4-Lite     ── FPGA内部寄存器映射接口
AXI4 Full     ── FPGA内部高性能存储器映射接口
RGMII/JESD204 ── FPGA与外部芯片之间的物理接口

例如UDP Payload进入FPGA后,可以从RGMII恢复成GMII,再由MAC转换为AXI4-Stream;AXI DMA随后把Stream数据转换成AXI Full写事务并存入DDR。

1.2 三类AXI的直观区别

特性 AXI4-Stream AXI4-Lite AXI4 Memory-Mapped(Full)
是否有地址
是否存储器映射
传输方向 单个接口单向 可读写 可读写
Burst 连续流,无固定上限概念 不支持Burst 支持Burst
典型数据宽度 任意常用宽度 常见32/64 bit 常见32~512 bit
接口复杂度
典型用途 视频、ADC、FFT、UDP Payload 控制和状态寄存器 DDR、BRAM、大块DMA数据
关注重点 帧边界和反压 地址译码与寄存器访问 Burst、Outstanding、顺序和吞吐率

二、AXI最核心的VALID/READY握手

虽然三类AXI的信号数量差异很大,但数据传输都建立在相同的VALID/READY握手规则上。

text 复制代码
VALID:发送端声明当前Payload有效
READY:接收端声明当前周期能够接收
传输发生:VALID && READY == 1

2.1 正常握手

text 复制代码
ACLK      _/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_
VALID     ____/‾‾‾‾‾‾‾‾‾‾‾\____________
READY     ________/‾‾‾‾‾‾‾‾‾\__________
PAYLOAD   ----<D0><D0><D1><D2><D3>--------
                    ↑   ↑   ↑   ↑
                  有效传输发生

发送端在VALID=1、READY=0时必须保持Payload稳定。Payload可以是TDATA,也可以是一组地址、长度和Burst属性信号。

2.2 VALID不能等待READY

发送端一旦有数据,就应独立拉高VALID。不能写成"看到READY之后才拉高VALID",否则当接收端也等待VALID时会形成死锁。

错误关系:

text 复制代码
发送端:等READY=1才拉VALID
接收端:等VALID=1才拉READY
结果:  两边永久等待

推荐规则:

text 复制代码
发送端控制VALID和Payload
接收端控制READY
双方都不能假定对方一定先动作

2.3 状态机只在握手时前进

verilog 复制代码
wire transfer = valid_reg && ready;

always @(posedge clk) begin
    if (rst) begin
        count <= 0;
    end else if (transfer) begin
        count <= count + 1'b1;
    end
end

如果只根据VALID增加计数,当READY拉低时,同一个数据会被错误地重复计数。

2.4 不同通道相互独立

AXI Memory-Mapped包含多个通道。每个通道都有自己的VALID/READY握手,不能假定地址和数据一定在同一个时钟周期完成。

这条规则非常重要,后文AXI Full和AXI-Lite的写通道设计都会用到。


三、AXI4-Stream协议

AXI4-Stream简称AXIS,用于从数据源向数据接收端连续传输数据。它没有地址阶段,也不要求接收端把数据存到某个地址。

text 复制代码
AXIS Source                                     AXIS Sink
┌────────────┐  TDATA/TKEEP/TLAST/TUSER       ┌────────────┐
│ UDP RX     │ ──────────────────────────────► │ AXIS FIFO  │
│            │ ───────── TVALID ─────────────► │            │
│            │ ◄──────── TREADY ────────────── │            │
└────────────┘                                 └────────────┘

3.1 常用信号

信号 方向 是否必需 说明
ACLK 输入 接口时钟
ARESETn 输入 视接口定义 低有效复位
TDATA Source→Sink 常用 数据
TVALID Source→Sink 数据和伴随信号有效
TREADY Sink→Source 可选/常用 接收端准备好
TKEEP Source→Sink 可选 每个字节是否有效
TSTRB Source→Sink 可选 字节属性/有效限定
TLAST Source→Sink 可选 一包或一帧结束
TUSER Source→Sink 可选 用户自定义旁带信息
TID Source→Sink 可选 数据流标识
TDEST Source→Sink 可选 目的路由标识

3.2 最小接口

简单的8 bit数据流可以只保留:

verilog 复制代码
input  wire [7:0] s_axis_tdata;
input  wire       s_axis_tvalid;
output wire       s_axis_tready;
input  wire       s_axis_tlast;

3.3 AXIS没有"读"和"写"的概念

AXIS接口本身只有数据流方向:Source向Sink发送。

如果模块需要双向传输,就使用两组相反方向的AXIS:

text 复制代码
模块A TX AXIS ─────────► 模块B RX AXIS
模块A RX AXIS ◄───────── 模块B TX AXIS

Ethernet、Aurora、PCIe等接口通常都有独立的TX Stream和RX Stream。

3.4 TKEEP的作用

假设数据宽度为64 bit,每拍包含8 Byte。当最后一拍只有3 Byte有效时:

text 复制代码
TDATA = xx_xx_xx_xx_xx_D2_D1_D0
TKEEP = 8'b0000_0111
TLAST = 1

TKEEP[i]=1表示对应的TDATA[8*i +: 8]有效。8 bit数据路径通常没有必要单独设置TKEEP,因为每拍只有一个字节。

3.5 TLAST的作用

AXIS可以是无边界连续流,也可以由TLAST划分数据包。

text 复制代码
TDATA   <A0><A1><A2><A3><B0><B1><B2>
TLAST   ___________/‾\________/‾\____
         Frame A结束    Frame B结束

对于UDP、Ethernet、DMA包和视频行,TLAST通常非常重要。若数据和TLAST错位一拍,后级可能把两个包拼在一起,或把一个包拆成两包。

3.6 AXIS反压

当Sink内部FIFO快满时,可以拉低TREADY

text 复制代码
TVALID    /‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\
TREADY    /‾‾‾\______/‾‾‾‾‾‾‾‾\
TDATA     <D0><D1><D2保持><D3><D4>
                      ↑
             READY=0期间保持不变

但要注意,并不是所有外部数据源都能接受反压。例如ADC和RGMII PHY可能持续产生数据,不能因为内部TREADY=0就停止。因此必须在不可暂停的数据源后放置足够深的FIFO,并设计溢出处理。

3.7 AXI4-Stream应用场景

场景 为什么适合AXIS
ADC采样数据 数据连续产生,不需要逐拍地址
DAC播放数据 连续流向输出,强调吞吐与时序
FFT/FIR/DDS 算法IP原生使用流式数据
UDP Payload 具有明确包边界,可使用TLAST
Ethernet MAC TX/RX帧天然是单向数据流
AXI DMA S2MM/MM2S Stream与存储器映射之间转换
视频像素流 每拍传输像素,TUSER/TLAST表示帧/行边界
Aurora/高速串行 高吞吐、顺序数据,无需存储器地址

3.8 一个简单的AXIS寄存器切片

下面给出单级Elastic Buffer的核心思路。它允许在下游暂停时保存一拍数据。

verilog 复制代码
assign s_axis_tready = !m_axis_tvalid_reg || m_axis_tready;

always @(posedge clk) begin
    if (rst) begin
        m_axis_tvalid_reg <= 1'b0;
    end else if (s_axis_tready) begin
        m_axis_tvalid_reg <= s_axis_tvalid;
        if (s_axis_tvalid) begin
            m_axis_tdata_reg <= s_axis_tdata;
            m_axis_tlast_reg <= s_axis_tlast;
        end
    end
end

工程中可以直接使用AXI Register Slice或AXIS FIFO IP,不必重复造轮子;但理解这一结构有助于定位组合路径过长和反压错误。


四、AXI4-Lite协议

AXI4-Lite是AXI4 Memory-Mapped的轻量子集。它保留地址、数据和响应通道,但不支持Burst,每次事务只传输一个数据拍。

最典型的用途是处理器访问IP核的控制寄存器。

text 复制代码
MicroBlaze
    │ AXI4-Lite
    ▼
┌──────────────────────────────┐
│ 自定义IP寄存器               │
│ 0x00 CONTROL:启动/停止      │
│ 0x04 STATUS :忙/完成/错误   │
│ 0x08 LENGTH :传输长度       │
│ 0x0C IRQ_EN :中断使能       │
└──────────────────────────────┘

4.1 五个独立通道

AXI4-Lite读写由五个通道组成:

通道 主要信号 方向 功能
写地址AW AWADDR/AWVALID/AWREADY Initiator→Target 发送写地址
写数据W WDATA/WSTRB/WVALID/WREADY Initiator→Target 发送写数据
写响应B BRESP/BVALID/BREADY Target→Initiator 返回写结果
读地址AR ARADDR/ARVALID/ARREADY Initiator→Target 发送读地址
读数据R RDATA/RRESP/RVALID/RREADY Target→Initiator 返回读数据和结果
text 复制代码
写操作:AW地址 ─┐
                 ├──► Target更新寄存器 ──► B响应
        W数据  ──┘

读操作:AR地址 ─────► Target读取寄存器 ──► R数据/响应

4.2 写地址和写数据相互独立

以下三种顺序都可能发生:

text 复制代码
情况A:AW先完成,W后完成
情况B:W先完成,AW后完成
情况C:AW和W同周期完成

Target必须分别记录AW与W握手状态,在地址和数据都已接收后才能执行一次完整写操作并产生BVALID

错误做法:

verilog 复制代码
// 错误示例:强制要求AWVALID和WVALID同周期出现
if (s_axi_awvalid && s_axi_wvalid) begin
    reg0 <= s_axi_wdata;
end

这种写法可能在某些简单仿真中工作,但不满足独立通道规则。

4.3 WSTRB字节写使能

32 bit AXI-Lite接口通常具有4 bit WSTRB

text 复制代码
WSTRB[0]控制WDATA[7:0]
WSTRB[1]控制WDATA[15:8]
WSTRB[2]控制WDATA[23:16]
WSTRB[3]控制WDATA[31:24]

寄存器更新应考虑字节使能:

verilog 复制代码
integer i;
for (i = 0; i < 4; i = i + 1) begin
    if (s_axi_wstrb[i])
        slv_reg0[i*8 +: 8] <= s_axi_wdata[i*8 +: 8];
end

如果忽略WSTRB,软件进行8 bit或16 bit写访问时可能错误覆盖其他字节。

4.4 响应码

常见的BRESP/RRESP为2 bit:

编码 名称 含义
2'b00 OKAY 正常完成
2'b01 EXOKAY 独占访问正常完成,AXI-Lite通常不使用
2'b10 SLVERR Target内部访问错误
2'b11 DECERR 地址没有被正确译码

简单自定义IP通常返回OKAY。对于非法寄存器地址,更严谨的设计可以返回SLVERR或由互连返回DECERR,而不是悄悄返回0。

4.5 AXI4-Lite应用场景

场景 典型寄存器内容
AXI GPIO 输入值、输出值、方向和中断
AXI Timer 计数值、周期、使能和中断
自定义采集IP 启动、长度、触发模式和完成状态
AXI DMA控制口 描述符地址、长度、状态
Ethernet控制 MAC配置、统计、使能和复位
MIG/系统管理 配置和状态,不承载大块DDR数据
中断控制器 中断使能、挂起和清除
SPI/IIC/UART 控制寄存器与少量数据寄存器

4.6 为什么不能用AXI-Lite传大数据

AXI-Lite每次只传一拍,每个数据拍都需要地址和响应开销。即使数据宽度为32 bit,连续搬运几MB数据也会占用大量事务和处理器时间。

因此:

text 复制代码
寄存器配置、状态查询     → AXI4-Lite
大量连续内存数据搬运     → AXI4 Full或DMA
连续无地址数据流         → AXI4-Stream

五、AXI4 Memory-Mapped(AXI Full)

严格来说,AXI规范中通常称为AXI4 Memory-Mapped。Vivado工程师为了和AXI4-Lite区分,经常称其为AXI Full。

AXI Full同样具有五个独立通道,但增加了Burst、ID、传输大小、Burst类型以及更多属性信号,适合高吞吐访问DDR、BRAM或PCIe地址空间。

5.1 五个通道

text 复制代码
                         AXI4 Memory-Mapped

Initiator                                              Target
┌────────────┐ AWADDR/AWLEN/AWSIZE/AWBURST/ID         ┌────────────┐
│ AXI DMA    │ ───────────── Write Address ─────────► │ MIG DDR4   │
│            │ WDATA/WSTRB/WLAST                       │            │
│            │ ───────────── Write Data ────────────► │            │
│            │ ◄──────────── Write Response ───────── │            │
│            │ ARADDR/ARLEN/ARSIZE/ARBURST/ID         │            │
│            │ ───────────── Read Address ──────────► │            │
│            │ ◄──────────── Read Data/RLAST ──────── │            │
└────────────┘                                        └────────────┘

5.2 关键写地址信号

信号 说明
AWID 写事务ID
AWADDR Burst首地址
AWLEN Burst拍数减1
AWSIZE 每拍传输字节数的log2
AWBURST FIXED、INCR或WRAP
AWLOCK 独占访问属性
AWCACHE Cache/Buffer相关属性
AWPROT 特权、安全和指令/数据属性
AWQOS 服务质量属性
AWVALID/AWREADY 写地址握手

5.3 关键写数据和响应信号

信号 说明
WDATA 写数据
WSTRB 每个字节的写使能
WLAST 当前拍为Burst最后一拍
WVALID/WREADY 写数据握手
BID 写响应对应的事务ID
BRESP 写响应状态
BVALID/BREADY 写响应握手

5.4 关键读地址和读数据信号

信号 说明
ARID 读事务ID
ARADDR Burst首地址
ARLEN Burst拍数减1
ARSIZE 每拍传输字节数的log2
ARBURST Burst类型
RDATA 读数据
RID 返回数据对应的事务ID
RRESP 读响应
RLAST 当前拍为Burst最后一拍
RVALID/RREADY 读数据握手

六、AXI Full的Burst传输

Burst允许一个地址阶段对应多个数据拍,是AXI Full实现高吞吐的关键。

6.1 AxLEN

AXI4中的AWLEN/ARLEN为8 bit,表示:

text 复制代码
Burst拍数 = AxLEN + 1

例如:

AxLEN 实际拍数
0 1 Beat
3 4 Beats
15 16 Beats
255 256 Beats

需要结合Burst类型理解上述上限:INCR Burst最多可以达到256 Beats;FIXED Burst通常限制在16 Beats以内;WRAP Burst的长度只能是2、4、8或16 Beats。实际设计还要受MIG、SmartConnect和目标IP参数的进一步限制。

6.2 AxSIZE

AWSIZE/ARSIZE表示每拍字节数:

text 复制代码
每拍字节数 = 2^AxSIZE

例如64 bit数据总线的满宽传输:

text 复制代码
64 bit = 8 Byte,因此AxSIZE = 3

128 bit数据总线:

text 复制代码
128 bit = 16 Byte,因此AxSIZE = 4

6.3 AxBURST

编码 类型 地址变化
2'b00 FIXED 每拍地址不变
2'b01 INCR 每拍地址按传输大小递增
2'b10 WRAP 地址递增并在规定边界回绕
2'b11 Reserved 不使用

大块DDR搬运最常用INCR Burst。

6.4 Burst地址计算

假设:

text 复制代码
AWADDR  = 0x1000
AWSIZE  = 3        // 每拍8 Byte
AWLEN   = 3        // 共4拍
AWBURST = INCR

则数据拍对应地址为:

text 复制代码
Beat 0:0x1000 ~ 0x1007
Beat 1:0x1008 ~ 0x100F
Beat 2:0x1010 ~ 0x1017
Beat 3:0x1018 ~ 0x101F,WLAST=1

一次地址握手即可传输32 Byte,大幅降低了地址开销。

6.5 Burst不能跨越4 KB边界

AXI4规定一个Burst不能跨越4 KB地址边界。

text 复制代码
允许:0x0F00开始,传输到0x0FFF以内
禁止:同一个Burst从0x0FF0跨到0x1000以后

计算当前地址到下一个4 KB边界的剩余字节数:

verilog 复制代码
wire [12:0] bytes_to_4k = 13'h1000 - {1'b0, current_addr[11:0]};

Burst生成器需要选择以下三者中的最小值:

text 复制代码
本次计划最大Burst字节数
剩余总传输字节数
当前地址到4 KB边界的字节数

6.6 WLASTRLAST

WLAST必须与写Burst最后一个有效数据拍同时握手;Target在读Burst最后一拍返回RLAST

text 复制代码
WVALID  /‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\
WREADY  /‾‾‾‾\__/‾‾‾‾‾‾‾‾\
WDATA   <D0><D1保持><D2><D3>
WLAST   ________________/‾\
                         ↑ 最后一拍握手

计数器必须在WVALID && WREADY时递增,不能按时钟周期递增。


七、AXI Full的并行能力

AXI Full并不是简单的"地址后跟数据"。五个通道可以并行工作,允许多个事务在系统中等待完成。

7.1 读写并行

读通道和写通道彼此独立:

text 复制代码
同一时间可以:
写地址通道提交Burst N+1
写数据通道发送Burst N
写响应通道接收Burst N-1的结果
读地址通道提交另一个读Burst
读数据通道返回更早的读Burst

7.2 Outstanding事务

Outstanding表示地址请求已经发出,但数据或响应尚未全部返回。

text 复制代码
AR请求0 ───────────────►
AR请求1 ───────────────►
AR请求2 ───────────────►
          ◄──── R数据0
          ◄──── R数据1
          ◄──── R数据2

多个Outstanding可以隐藏DDR访问延迟,提高总线利用率,但会显著增加控制复杂度。

7.3 ID和顺序

事务ID用于区分多个未完成事务。相同ID通常需要保持规定的顺序,不同ID可以支持更灵活的返回顺序,具体还受互连和Target能力限制。

初学自定义AXI Master时,建议:

text 复制代码
第一版固定ID
一次只允许一个写Burst和一个读Burst未完成
先保证协议正确
再逐步增加Outstanding数量

7.4 地址与写数据不能错误绑定

AW通道与W通道独立。AXI4取消了写数据ID,写数据必须按照写地址被接受的顺序组织。自定义Master需要维护清晰的Burst队列,不能让W数据与错误的AW事务对应。


八、三种接口在工程中如何配合

一个IP核可以同时具有三种AXI接口。

以AXI DMA为例:

text 复制代码
                           AXI DMA
┌────────────────────────────────────────────────────────────┐
│ S_AXI_LITE:处理器配置源地址、目的地址、长度和启动       │
│ M_AXI_MM2S:通过AXI Full从DDR读取                         │
│ M_AXIS_MM2S:通过AXIS向用户逻辑输出                       │
│ S_AXIS_S2MM:通过AXIS接收用户逻辑数据                     │
│ M_AXI_S2MM:通过AXI Full写入DDR                            │
└────────────────────────────────────────────────────────────┘

这说明三类接口不是竞争关系,而是分工关系:

text 复制代码
AXI-Lite:控制面
AXI-Stream:实时数据面
AXI Full:存储器数据面

8.1 FPGA UDP采集系统

text 复制代码
RGMII → UDP Stack → AXIS FIFO → AXI DMA → AXI Full → DDR4
                          ▲          ▲
                          │          │ AXI-Lite配置
                          └──────── MicroBlaze

8.2 ADC信号处理系统

text 复制代码
ADC → AXIS → DDC → FIR → FFT → AXIS FIFO → DMA → DDR
              ▲      ▲     ▲
              └──── AXI-Lite寄存器配置 ──── MicroBlaze

8.3 波形播放系统

text 复制代码
DDR → AXI Full读 → DMA MM2S → AXIS FIFO → DAC接口
         ▲
         └── MicroBlaze通过AXI-Lite配置地址和长度

九、基于AXI Full的工程目标

下面设计一个适用于KU5P与DDR4 MIG的高速数据缓存工程。

9.1 功能需求

  1. 外部数据以AXI4-Stream形式输入;
  2. 数据打包后通过自定义AXI4 Master突发写入DDR4;
  3. 支持从DDR4突发读出,并通过AXI4-Stream输出;
  4. MicroBlaze通过AXI4-Lite配置起始地址、长度和控制位;
  5. 支持写完成、读完成、总线错误和FIFO溢出中断;
  6. 通过ILA与AXI Protocol Checker调试总线行为。

9.2 总体架构

text 复制代码
                                  KU5P FPGA
┌───────────────────────────────────────────────────────────────────────┐
│                                                                       │
│ 外部数据/UDP RX                                                       │
│      │ AXI4-Stream                                                    │
│      ▼                                                                │
│ ┌──────────────┐    ┌────────────────────┐                            │
│ │ Input FIFO   │───►│ AXI Full Write     │──┐                         │
│ │ +宽度转换    │    │ Master/Burst Engine│  │                         │
│ └──────────────┘    └────────────────────┘  │ AXI4-MM                 │
│                                             ▼                         │
│                                      ┌──────────────┐    ┌──────────┐ │
│ MicroBlaze ──AXI-Lite──┐             │ SmartConnect│───►│ MIG DDR4 │ │
│                        ▼             └──────────────┘    └──────────┘ │
│                 ┌──────────────┐             ▲                        │
│                 │ Control/Status│             │ AXI4-MM                │
│                 │ Registers    │             │                        │
│                 └──────────────┘    ┌────────────────────┐            │
│                        │            │ AXI Full Read      │            │
│                        └───────────►│ Master/Burst Engine│            │
│                                     └─────────┬──────────┘            │
│                                               ▼ AXI4-Stream           │
│                                        ┌──────────────┐               │
│                                        │ Output FIFO  │──► 用户逻辑   │
│                                        └──────────────┘               │
│                                                                       │
└───────────────────────────────────────────────────────────────────────┘

9.3 为什么架构核心是AXI Full

DDR4是随机访问存储器,需要明确地址;同时数据量大,需要Burst降低地址开销。因此写引擎和读引擎都作为AXI Full Initiator访问MIG。

AXI4-Stream只负责模块边界的连续数据,AXI4-Lite只负责寄存器配置,真正承担大块数据搬运的是AXI Full。


十、工程模块划分

text 复制代码
axi_ddr_buffer_top
├── axi_lite_regs
│   ├── 控制/状态寄存器
│   └── 中断生成
├── axis_input_fifo
│   └── AXIS宽度转换与跨时钟域
├── axi_write_master
│   ├── 写命令队列
│   ├── AW Burst生成器
│   ├── W数据发送器
│   └── B响应检查
├── axi_read_master
│   ├── 读命令队列
│   ├── AR Burst生成器
│   ├── R数据接收器
│   └── AXIS输出打包
├── axis_output_fifo
├── axi_smartconnect
├── ddr4_mig
└── debug
    ├── ILA
    ├── AXI Protocol Checker
    └── 性能计数器

10.1 建议寄存器表

偏移地址 名称 属性 功能
0x00 CONTROL R/W bit0写启动,bit1读启动,bit2软复位
0x04 STATUS R busy、done、error、overflow
0x08 WR_BASE_ADDR_L R/W 写起始地址低32 bit
0x0C WR_BASE_ADDR_H R/W 写起始地址高32 bit
0x10 WR_LENGTH R/W 写入总字节数
0x14 RD_BASE_ADDR_L R/W 读起始地址低32 bit
0x18 RD_BASE_ADDR_H R/W 读起始地址高32 bit
0x1C RD_LENGTH R/W 读出总字节数
0x20 BURST_CFG R/W 最大Burst拍数
0x24 IRQ_ENABLE R/W 中断使能
0x28 IRQ_STATUS R/W1C 中断状态,写1清除
0x2C ERROR_ADDR_L R 首次错误地址低32 bit
0x30 ERROR_ADDR_H R 首次错误地址高32 bit
0x34 PERF_CYCLES R 传输周期计数
0x38 PERF_BYTES R 有效字节计数

控制寄存器适合AXI-Lite,不应混入AXI Full大数据通路。


十一、AXI Full写引擎设计

11.1 写入流程

text 复制代码
IDLE
  │ start
  ▼
计算Burst长度和4 KB边界
  │
  ├──► AW通道提交地址
  │
  ├──► W通道从FIFO发送数据,末拍产生WLAST
  │
  └──► 等待B响应
            │
      ┌─────┴─────┐
      │BRESP=OKAY │──► 更新地址/剩余长度 ──► 下一Burst
      │BRESP错误  │──► 记录地址并进入ERROR
      └───────────┘

11.2 不要把写引擎简单做成单状态机

AW、W和B通道独立,工程上最好分别维护通道状态,再用事务上下文协调。

text 复制代码
AW控制器:负责地址、长度和AWVALID保持
W控制器: 负责数据、WSTRB、WLAST和WVALID保持
B控制器: 负责响应计数和错误检查

第一版可以限制为一次只处理一个Burst:

text 复制代码
提交AW → 发送完整W Burst → 等待B → 下一Burst

这种方式性能不是最高,但逻辑容易验证。协议跑通后,再让AW提前提交下一Burst以提高吞吐。

11.3 写地址通道骨架

verilog 复制代码
always @(posedge aclk) begin
    if (!aresetn) begin
        m_axi_awvalid <= 1'b0;
    end else begin
        if (issue_aw) begin
            m_axi_awaddr  <= burst_addr;
            m_axi_awlen   <= burst_beats - 1'b1;
            m_axi_awsize  <= AXI_SIZE;
            m_axi_awburst <= 2'b01; // INCR
            m_axi_awvalid <= 1'b1;
        end else if (m_axi_awvalid && m_axi_awready) begin
            m_axi_awvalid <= 1'b0;
        end
    end
end

AWVALID=1、AWREADY=0时,AWADDR/AWLEN/AWSIZE/AWBURST必须保持稳定。

11.4 写数据通道骨架

verilog 复制代码
wire w_fire = m_axi_wvalid && m_axi_wready;

always @(posedge aclk) begin
    if (!aresetn) begin
        m_axi_wvalid <= 1'b0;
        m_axi_wlast  <= 1'b0;
        beat_count   <= 0;
    end else begin
        if (load_first_beat || (w_fire && !m_axi_wlast)) begin
            m_axi_wdata  <= fifo_data;
            m_axi_wstrb  <= fifo_keep;
            m_axi_wvalid <= fifo_valid;
            m_axi_wlast  <= (beat_count == burst_beats-1);
        end

        if (w_fire)
            beat_count <= beat_count + 1'b1;

        if (w_fire && m_axi_wlast) begin
            m_axi_wvalid <= 1'b0;
            m_axi_wlast  <= 1'b0;
        end
    end
end

上面是结构示意,实际工程还需要确保只有在AXI寄存器能够接收下一拍时才弹出FIFO,并正确处理FIFO与AXI总线的数据宽度差异。

11.5 B响应处理

verilog 复制代码
assign m_axi_bready = 1'b1;

always @(posedge aclk) begin
    if (!aresetn) begin
        write_error <= 1'b0;
    end else if (m_axi_bvalid && m_axi_bready) begin
        if (m_axi_bresp != 2'b00)
            write_error <= 1'b1;
    end
end

不能在最后一个W数据拍发送完后就宣布"写完成"。只有收到对应B响应,才能确认Target已经完成该写事务。


十二、AXI Full读引擎设计

12.1 读取流程

text 复制代码
IDLE
  │ start
  ▼
计算Burst长度和4 KB边界
  │
  ▼
AR通道提交地址
  │
  ▼
R通道接收数据 ──► Output FIFO
  │
  ├── RRESP错误:记录错误
  └── RLAST:完成本Burst,更新地址和剩余长度

12.2 AR地址通道

verilog 复制代码
always @(posedge aclk) begin
    if (!aresetn) begin
        m_axi_arvalid <= 1'b0;
    end else begin
        if (issue_ar) begin
            m_axi_araddr  <= burst_addr;
            m_axi_arlen   <= burst_beats - 1'b1;
            m_axi_arsize  <= AXI_SIZE;
            m_axi_arburst <= 2'b01;
            m_axi_arvalid <= 1'b1;
        end else if (m_axi_arvalid && m_axi_arready) begin
            m_axi_arvalid <= 1'b0;
        end
    end
end

12.3 R通道与输出FIFO

verilog 复制代码
assign m_axi_rready = !out_fifo_full;

always @(posedge aclk) begin
    if (m_axi_rvalid && m_axi_rready) begin
        out_fifo_wr_en <= 1'b1;
        out_fifo_wdata <= m_axi_rdata;

        if (m_axi_rresp != 2'b00)
            read_error <= 1'b1;

        if (m_axi_rlast)
            burst_done <= 1'b1;
    end
end

当Output FIFO接近满时,读引擎可以拉低RREADY施加反压。但在发出大量Outstanding读请求前,必须保证FIFO有能力容纳后续可能返回的数据,否则会造成系统长时间阻塞。

12.4 检查RLAST位置

除了接收Target给出的RLAST,读引擎还应根据ARLEN维护期望拍数:

text 复制代码
RLAST提前出现 → Early Last错误
期望最后一拍没有RLAST → Missing Last错误
RRESP非OKAY → 总线响应错误

十三、地址、长度和对齐设计

13.1 地址对齐

若AXI数据宽度为128 bit,则满宽每拍传输16 Byte,推荐起始地址按16 Byte对齐:

text 复制代码
合法对齐地址:0x0000、0x0010、0x0020......
非对齐地址:  0x0004、0x0008......

AXI协议能够描述部分非对齐访问,但MIG、互连和自定义Master的支持范围需要确认。第一版工程建议只允许自然对齐地址。

13.2 尾包不足一个数据拍

最后剩余数据不足总线宽度时,写通道通过WSTRB标记有效字节:

text 复制代码
128 bit总线,最后剩6 Byte:
WSTRB = 16'b0000_0000_0011_1111

若数据来自AXI4-Stream,可以将最后一拍的TKEEP映射为AXI Full的WSTRB

13.3 长度单位必须统一

工程中常见三种长度:

text 复制代码
软件配置长度:Byte
AXI AxLEN:    Beat数减1
FIFO计数:     可能是Word或Beat

建议控制寄存器始终使用Byte为单位,并在Burst生成器内部换算,避免软件和RTL对长度单位理解不同。


十四、时钟域与复位设计

DDR MIG用户时钟、MicroBlaze时钟和采集时钟不一定相同。

text 复制代码
ADC/UDP时钟域 ── AXIS异步FIFO ──► AXI/MIG时钟域
                                         ▲
MicroBlaze时钟域 ─ AXI Clock Converter ──┘

14.1 数据跨时钟域

  • 连续多bit数据使用异步FIFO;
  • 单bit脉冲使用Pulse Synchronizer或Toggle同步;
  • 多bit配置先锁存,再通过握手跨域;
  • 不要对多bit总线的每一位单独打两拍。

14.2 复位原则

  • 每个时钟域分别同步释放复位;
  • 复位时所有AXI输出VALID清零;
  • 不能依赖对端READY清除本端VALID;
  • MIG校准完成init_calib_complete前,不发起DDR访问;
  • 软件启动前清空FIFO、计数器和错误状态。

十五、带宽计算与Burst优化

15.1 理论带宽

text 复制代码
理论带宽 = 数据位宽 / 8 × 时钟频率

例如128 bit、200 MHz:

text 复制代码
128 / 8 × 200 MHz = 3.2 GB/s

这是总线每拍都完成有效数据传输时的上限,不包含DDR刷新、Bank冲突、仲裁和地址响应间隙。

15.2 实际效率

text 复制代码
总线效率 = 有效传输Beat数 / 总时钟周期数
有效带宽 = 理论带宽 × 总线效率

例如3.2 GB/s理论带宽,总线数据有效率为75%:

text 复制代码
有效带宽约为2.4 GB/s

15.3 提升性能的方法

  1. 使用较长INCR Burst,减少地址阶段占比;
  2. 保证地址对齐;
  3. 增加输入和输出FIFO深度;
  4. 允许适量Outstanding事务隐藏DDR延迟;
  5. 使用更宽的数据通路减少每个Byte的控制开销;
  6. 避免Burst跨4 KB边界;
  7. 将读写地址安排为连续区域,提高DDR行命中率;
  8. 通过AXI Performance Monitor或自定义计数器测量,而不是只看理论值。

15.4 不要盲目增加Burst长度

Burst越长,单次仲裁占用时间越长,可能增加其他Master的等待时间。在MicroBlaze、DMA和自定义Master共享DDR时,需要在吞吐率和访问延迟之间折中。


十六、Vivado Block Design实现建议

16.1 主要IP

IP 用途
MicroBlaze 软件控制和状态管理
AXI SmartConnect 多Initiator、多Target互连、宽度/时钟适配
MIG DDR4 DDR4控制器及AXI4 Target接口
AXI BRAM Controller 小容量片上缓冲或测试存储器
AXI DMA/DataMover Stream与Memory-Mapped转换
AXI Clock Converter Memory-Mapped跨时钟域
AXIS Data FIFO Stream缓存和跨时钟域
AXI Protocol Checker 在线检查协议违规
ILA 观察握手、地址、数据和响应
AXI Performance Monitor 测量延迟与吞吐率

16.2 地址空间示例

text 复制代码
0x8000_0000 ~ 0xBFFF_FFFF:DDR4数据区
0x44A0_0000 ~ 0x44A0_FFFF:自定义AXI-Lite控制寄存器
0x4120_0000 ~ 0x4120_FFFF:AXI GPIO
0x41E0_0000 ~ 0x41E0_FFFF:AXI Interrupt Controller

实际地址应以Vivado Address Editor分配结果为准。

16.3 SmartConnect连接

text 复制代码
自定义AXI写Master ─┐
自定义AXI读Master ─┼──► SmartConnect ──► MIG S_AXI
MicroBlaze缓存接口 ─┤
JTAG to AXI Master ─┘

如果多个Initiator共享DDR,SmartConnect负责地址译码和仲裁;但系统级带宽仍受DDR控制器和访问模式限制。


十七、仿真验证方法

17.1 AXI4-Stream测试

至少覆盖:

  • Source连续发送、Sink始终Ready;
  • Sink随机拉低Ready;
  • 单字节帧和长帧;
  • 最后一拍部分字节有效;
  • 连续帧无空闲间隔;
  • TLAST/TKEEP/TUSER随反压保持稳定。

17.2 AXI4-Lite测试

至少覆盖:

  • AW先于W;
  • W先于AW;
  • AW与W同周期;
  • BREADY延迟;
  • ARREADY延迟;
  • RREADY延迟;
  • 不同WSTRB组合;
  • 非法地址和复位中断事务。

17.3 AXI Full写测试

用例 检查重点
单Beat写 AWLEN=0、WLAST正确
16/256 Beat Burst 长Burst计数
随机WREADY WDATA/WLAST保持
4 KB边界附近 自动拆分Burst
非整拍尾数据 WSTRB正确
BRESP错误注入 错误记录与停止策略
连续Burst 地址递增和响应计数

17.4 AXI Full读测试

  • 随机ARREADYRVALID间隙;
  • 正确/提前/缺失RLAST
  • RRESP错误;
  • 输出FIFO反压;
  • 多Burst地址连续性;
  • 4 KB边界拆分;
  • 写入后读回逐字节比较。

17.5 Scoreboard

最直接的验证方式是建立参考存储器模型:

text 复制代码
随机生成输入数据
      │
      ├──► DUT通过AXI写入Memory Model
      │
      └──► Python/SystemVerilog参考数组

DUT读回数据 ──► 与参考数组逐字节比较

只观察波形看起来"差不多"并不足以证明大块数据没有错位。


十八、ILA调试信号建议

18.1 AXI Full写通道

text 复制代码
AWADDR、AWLEN、AWSIZE、AWBURST、AWVALID、AWREADY
WDATA、WSTRB、WLAST、WVALID、WREADY
BRESP、BVALID、BREADY
write_addr、remaining_bytes、beat_count、burst_count

18.2 AXI Full读通道

text 复制代码
ARADDR、ARLEN、ARSIZE、ARBURST、ARVALID、ARREADY
RDATA、RRESP、RLAST、RVALID、RREADY
read_addr、remaining_bytes、beat_count、fifo_level

18.3 推荐触发条件

text 复制代码
BRESP != OKAY
RRESP != OKAY
RLAST位置与期望不一致
FIFO overflow/underflow
VALID保持期间Payload变化
MIG init_calib_complete=0时出现AWVALID/ARVALID

使用AXI Protocol Checker能够比人工观察ILA更快发现握手和稳定性违规;ILA则用于进一步定位状态机为什么产生了违规信号。


十九、常见错误与定位

现象 常见原因 处理方法
AXIS随机丢字节 只判断TVALID,没有判断TREADY 仅在握手时更新计数和数据
AXIS死锁 Source等待READY才拉VALID VALID必须独立产生
TLAST错位 计数按时钟而不是按握手递增 使用TVALID && TREADY
AXI-Lite偶发写不进去 强制AW和W同周期 分别缓存地址和数据
字节写破坏整寄存器 忽略WSTRB 按字节更新寄存器
AXI Full写完但内存没更新 W结束后未等待B响应 以B握手确认事务完成
Burst末拍错误 WLAST/RLAST计数错误 以实际握手拍数计数
4 KB附近协议报错 Burst跨4 KB 在边界前拆分
长包错误、短包正常 FIFO不足或Burst切分错误 检查FIFO水位和剩余长度
DDR偶发卡死 MIG未校准完成就访问 等待init_calib_complete
吞吐远低于理论值 Burst太短、空拍多、Outstanding不足 统计VALID/READY利用率
开Cache后数据不一致 DMA与CPU Cache不一致 Flush/Invalidate或使用一致性策略
多Master延迟过大 SmartConnect仲裁和长Burst占用 调整Burst、QoS和端口结构

二十、MicroBlaze与AXI Full工程中的Cache问题

当MicroBlaze和DMA/自定义AXI Master同时访问DDR时,需要特别注意Cache一致性。

text 复制代码
MicroBlaze D-Cache ─┐
                    ├──► DDR同一缓冲区
AXI DMA/自定义Master┘

DMA写入DDR后,MicroBlaze的Cache中可能仍然保留旧数据;MicroBlaze修改了Cache但尚未写回DDR时,DMA又可能读到旧内容。

典型处理流程:

CPU准备数据给DMA读取

text 复制代码
CPU写缓冲区 → Flush Cache → 启动DMA → DMA读取DDR

DMA写数据给CPU读取

text 复制代码
启动DMA → DMA写DDR → DMA完成 → Invalidate Cache → CPU读取

因此,AXI Full总线本身传输正确,不代表软件看到的数据一定正确。Cache维护属于系统一致性问题,需要在软件驱动中明确处理。


二十一、三种AXI的选择原则

text 复制代码
问题1:数据是否需要访问明确地址?
  否 → AXI4-Stream
  是 ↓

问题2:是否只是少量寄存器、低频访问?
  是 → AXI4-Lite
  否 ↓

问题3:是否需要Burst、高吞吐或DDR访问?
  是 → AXI4 Memory-Mapped(AXI Full)

更简洁地说:

需求 选择
数据从A连续流向B AXI4-Stream
CPU配置几个寄存器 AXI4-Lite
大块数据读写DDR AXI Full
Stream数据存DDR AXIS + DMA/DataMover + AXI Full
CPU控制Stream算法IP AXI-Lite控制 + AXIS数据

二十二、推荐学习顺序

第一阶段:AXI4-Stream

先掌握TVALID/TREADY/TDATA/TLAST,实现一个可随机反压的AXIS FIFO或寄存器切片。

第二阶段:AXI4-Lite Target

使用Vivado模板创建4~16个控制寄存器,重点验证AW/W不同到达顺序和WSTRB。

第三阶段:单Burst AXI Full Master

只允许一个未完成事务,实现固定16 Beat的DDR写入和读回。

第四阶段:可变长度与4 KB拆分

加入总字节数、尾拍WSTRB、可变Burst和4 KB边界控制。

第五阶段:并行和性能

增加命令FIFO、多个Outstanding和性能计数器,并用随机反压Testbench验证。

不要在第一版就同时实现多ID、乱序返回和大量Outstanding。先建立一条完全符合协议、可自动校验的数据路径,再逐步提高性能。


总结

本文介绍了Vivado工程中最常见的三类AXI协议:

text 复制代码
AXI4-Stream:无地址、单向、适合连续数据和分组流
AXI4-Lite:  存储器映射、单拍、适合控制与状态寄存器
AXI4 Full:  存储器映射、支持Burst,适合DDR和大块数据搬运

三者共同遵循VALID/READY握手,但接口结构和工程目标不同。AXIS设计的重点是反压和帧边界;AXI-Lite设计的重点是五通道独立握手和寄存器访问;AXI Full设计的重点是Burst、4 KB边界、响应、Outstanding以及带宽利用率。

本文给出的AXI Full工程架构,将输入AXI4-Stream数据通过自定义Burst Master写入MIG DDR4,再由读Master取出并恢复为AXI4-Stream;MicroBlaze通过AXI-Lite完成配置。这个架构可以作为UDP数据缓存、ADC采集、波形播放以及大容量数据处理系统的通用基础。

对于实际工程,最重要的设计原则仍然是:

  1. 只有VALID和READY同时为1时,传输才真正发生;
  2. VALID等待期间,Payload必须保持稳定;
  3. AXI Memory-Mapped五个通道相互独立;
  4. 每个Burst不能跨越4 KB边界;
  5. 大块数据使用AXI Full,配置寄存器使用AXI-Lite,连续数据使用AXIS;
  6. 仿真必须加入随机反压、错误响应和逐字节Scoreboard;
  7. MicroBlaze、DMA和DDR共用缓冲区时必须处理Cache一致性。

掌握这三类接口后,Vivado中大量看似不同的IP核就会呈现出统一结构:控制走Lite,数据流走Stream,存储器访问走Full。复杂FPGA系统的连接关系也会因此清晰很多。

Fin


参考资料

  1. Arm AMBA AXI and ACE Protocol Specification(IHI 0022)
  2. Arm AMBA AXI4-Stream Protocol Specification(IHI 0051)
  3. AMD Vivado Design Suite AXI Reference Guide(UG1037)
  4. AMD AXI Interconnect
  5. AMD AXI4-Stream工作方式
相关推荐
自小吃多2 小时前
高云 FPGA 添加时序约束笔记
笔记·fpga开发
学习FPGA的电气小兴兴8 小时前
基于FPGA的CORDIC旋转模式实现sin和cos运算
fpga开发·信号处理·数字信号处理·verilog·fpga·verilog hdl·cordic
blue_ice .11 小时前
DDS原理及简易实现
开发语言·经验分享·笔记·嵌入式硬件·fpga开发
明德扬11 小时前
AD9653采集模块怎样连接FPGA底板?从ADC采样到数据处理
学习·fpga开发·fpga
上海芯聚电子1 天前
ADC与FPGA之间,如何减少板级设计复杂度?
fpga开发·adc
自小吃多1 天前
高云FPGA ModelSim中的仿真
笔记·嵌入式硬件·fpga开发
通信王661 天前
适用于 FPGA 和 SoC 的UltraFast 设计方法指南(1)-UG949
fpga开发
明德扬1 天前
AD9653采集模块、PCIe采集卡和完整采集系统怎么选?
fpga开发·fpga
智塑未来1 天前
FPGA开发板选型:研发交付能力怎么看
人工智能·fpga开发