本文承接前面的FPGA逻辑设计与UDP通信系列,系统介绍Vivado工程中最常见的三类AXI接口:AXI4-Stream、AXI4-Lite和AXI4 Memory-Mapped。其中,工程师经常将完整的AXI4 Memory-Mapped接口简称为"AXI Full",本文也沿用这一叫法。
前言
随着FPGA工程规模增加,模块之间不可能一直使用各自定义的data、valid、busy和done信号连接。
如果每个模块的接口都不相同,会出现以下问题:
- 同一个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 WLAST和RLAST
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 功能需求
- 外部数据以AXI4-Stream形式输入;
- 数据打包后通过自定义AXI4 Master突发写入DDR4;
- 支持从DDR4突发读出,并通过AXI4-Stream输出;
- MicroBlaze通过AXI4-Lite配置起始地址、长度和控制位;
- 支持写完成、读完成、总线错误和FIFO溢出中断;
- 通过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 提升性能的方法
- 使用较长INCR Burst,减少地址阶段占比;
- 保证地址对齐;
- 增加输入和输出FIFO深度;
- 允许适量Outstanding事务隐藏DDR延迟;
- 使用更宽的数据通路减少每个Byte的控制开销;
- 避免Burst跨4 KB边界;
- 将读写地址安排为连续区域,提高DDR行命中率;
- 通过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读测试
- 随机
ARREADY和RVALID间隙; - 正确/提前/缺失
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采集、波形播放以及大容量数据处理系统的通用基础。
对于实际工程,最重要的设计原则仍然是:
- 只有VALID和READY同时为1时,传输才真正发生;
- VALID等待期间,Payload必须保持稳定;
- AXI Memory-Mapped五个通道相互独立;
- 每个Burst不能跨越4 KB边界;
- 大块数据使用AXI Full,配置寄存器使用AXI-Lite,连续数据使用AXIS;
- 仿真必须加入随机反压、错误响应和逐字节Scoreboard;
- MicroBlaze、DMA和DDR共用缓冲区时必须处理Cache一致性。
掌握这三类接口后,Vivado中大量看似不同的IP核就会呈现出统一结构:控制走Lite,数据流走Stream,存储器访问走Full。复杂FPGA系统的连接关系也会因此清晰很多。
Fin