AXI4-Full是AXI协议族里最复杂的一种------支持突发传输、支持乱序、支持多机并行。
很多人以为AXI4-Full就是"AXI4-Lite + BURST",结果一写代码就踩坑:数据丢了、顺序乱了、WRAP传输直接卡死。
这篇把AXI4-Full最常见的4个致命坑全部拆开,配上核心代码片段。完整工程代码(DMA控制器、WRAP地址生成、窄传输处理、突发错误处理)可"私信"获取。
痛苦点一:突发长度算错,传输一半就断了
问题描述
你用AXI4-Full做DMA,从DDR读4KB数据到FPGA内部BRAM,结果只读了一半。
C
// 读4KB数据,32bit接口,1024次传输
AXI_DMA_Read(src_addr=0x10000000, length=4096);
仿真时发现:只读了512个数据(2KB),剩下的一半不见了。
根本原因
AXI4-Full的突发长度(AxLEN)编码规则:
| AxLEN7:0值 | 实际突发长度 | 说明 |
|---|---|---|
| 0x00 | 1次 | AxLEN=0表示1次传输 |
| 0x01 | 2次 | |
| 0x0F | 16次 | |
| 0xFF | 256次 | AXI4最大值 |
AxLEN = 实际传输次数 - 1
如果你要读1024次,必须设置AxLEN = 1023(0x3FF),但AXI4单突发最大256次(AxLEN=255),所以不能一次发完,必须拆成多个突发。
错误做法
verilog
// ❌ 错误:AxLEN直接填传输次数
assign arlen = 16'd1024; // ❌ 这表示1025次传输!AXI4会截断
assign arlen = 16'd256; // ❌ 这表示257次传输!
// ❌ 错误的突发拆分:
// 想读1024次,拆成4个突发:
assign arlen_0 = 16'd256; // 257次
assign arlen_1 = 16'd256; // 257次
assign arlen_2 = 16'd256; // 257次
assign arlen_3 = 16'd255; // 256次
// 总计:257+257+257+256 = 1027次 ← 多了3次!
正确做法(关键公式)
verilog
// ✅ 核心公式:AxLEN = beats - 1
// 4KB数据,32bit接口 → 1024 beats
// AXI4单突发最多256 beats → 拆成4个突发,每个arlen=255
// 最后一次不足256 beat时,arlen = (剩余beats) - 1
📌 完整DMA读控制器代码(包含突发拆分、地址累加、状态机)点击"阅读原文"获取。
💡 注意:AxLEN最大取值为0xFF(255),硬件IP/Interconnect会直接截断超长突发,不能一次下发1023。
关键计算公式:
| 参数 | 公式 |
|---|---|
| 总传输次数 | ceil(字节数 / (数据位宽/8)) |
| 突发次数 | ceil(总传输次数 / 最大突发长度) |
| AxLEN | 实际传输次数 - 1 |
痛苦点二:WRAP突发传输出错,地址跳回起点后数据乱了
问题描述
用WRAP模式读环形缓冲区,地址回绕后数据顺序全乱。
verilog
// 从地址0x100开始,突发长度16,WRAP边界256字节
assign araddr = 32'h00000100;
assign arlen = 8'd15; // 16次传输
assign arburst = 2'b10; // WRAP模式
仿真时发现:第12次传输后,地址不是0x110而是跳回了0x100!数据顺序全乱了。
根本原因
WRAP突发的地址计算规则和INCR完全不同:
| 突发模式 | 地址变化 | 适用场景 |
|---|---|---|
| FIXED (00) | 地址不变 | FIFO读写 |
| INCR (01) | 地址递增 | 普通内存访问 |
| WRAP (10) | 到达边界后回绕 | Cache行填充 |
WRAP边界 = 突发长度 × 传输字节数,且边界必须是2的幂。
错误做法
verilog
// ❌ 起始地址不在边界上
assign araddr = 32'h00000110; // 0x110,边界64字节,预期会回绕到0x100
正确做法(核心计算)
verilog
// ✅ WRAP边界 = (arlen+1) × (1 << arsize)
// 低位掩码 = 边界 - 1
// 当前地址 = 高位(起始地址 & ~掩码) + 低位((起始偏移 + 累加步长) & 掩码)
💡 工程建议 :实际项目中90%的场景用INCR即可,WRAP主要用于Cache行填充。如果不需要WRAP,直接用
arburst = 2'b01(INCR)。
WRAP使用场景总结:
| 场景 | 是否用WRAP | 原因 |
|---|---|---|
| 普通内存读写 | ❌ 用INCR | 简单可靠 |
| FIFO读写 | ❌ 用FIXED | 地址不变 |
| Cache行填充 | ✅ 用WRAP | 提升Cache性能 |
| 循环缓冲区 | 可选 | INCR+地址回绕更直观 |
痛苦点三:窄传输(Narrow Transfer)字节错位
问题描述
64bit AXI接口连接32bit外设,写入数据后读回错位。
verilog
// 64bit AXI接口
// 但外设寄存器是32bit
// 地址0x00: reg0 (32bit)
// 地址0x04: reg1 (32bit)
// 地址0x08: reg2 (32bit)
// 地址0x0C: reg3 (32bit)
你写入64bit数据到地址0x00,期望写到reg0和reg1,但读回来发现数据全错了。
根本原因
AXI的WSTRB(写选通)信号决定了哪些字节有效。窄传输是指:数据位宽大于实际传输大小,需要用WSTRB选择有效字节。
AXI读无RSTRB,读窄传输靠地址区分数据位置,地址bit2用来选择64bit接口中的高低32bit,这是窄读错位最高发的坑点。
错误做法
verilog
// ❌ 错误:忽略WSTRB,直接写整个64bit
assign reg0 = wdata31:0; // 忽略了WSTRB
assign reg1 = wdata63:32;
// 如果Master只想写reg0:
// wdata = 64'hXXXX_XXXX_1234_5678
// wstrb = 8'h0F (只有低4字节有效)
// 错误实现会把高32bit的垃圾数据也写入reg1
正确做法(核心逻辑)
verilog
// ✅ 写:根据WSTRB逐字节写入
if (wstrb0) reg07:0 <= wdata7:0;
if (wstrb1) reg015:8 <= wdata15:8;
// ... 类似处理其他字节
// ✅ 读:用地址bit2区分64bit中的高低32bit
assign rdata = (araddr2 == 1'b0) ? {regs1, regs0} : {regs3, regs2};
💡 关键点:
-
写操作:WSTRB决定哪些字节有效,无效字节不写入
-
读操作:没有RSTRB,用地址选择返回数据
-
地址对齐:64bit接口地址必须是8字节对齐
-
推荐做法:32bit外设直接用32bit AXI接口,避免窄传输
痛苦点四:突发传输中间出错,数据残缺怎么办
问题描述
你用AXI4-Full读取256次突发数据,读到一半RRESP返回了SLVERR:
verilog
// 读突发过程中
// Beat 0-127: RRESP = OKAY
// Beat 128: RRESP = SLVERR ← 错误!
// Beat 129-255: 还要继续吗?
根本原因
AXI4协议规定:突发传输一旦开始,必须完成所有Beats,即使中间有错误。
| 场景 | 规则 |
|---|---|
| 读突发出错 | Slave必须在剩余Beats返回RDATA(值不确定),RRESP=SLVERR或DECERR |
| 写突发出错 | Slave可以立即返回BRESP=SLVERR,但必须接受完所有W数据 |
| Master处理 | 检查RRESP/RESP,如果出错,丢弃整个突发数据 |
错误做法
verilog
// ❌ 错误1:读到错误后立即停止
always @(posedge aclk) begin
if (rvalid && rready) begin
if (rresp != 2'b00) begin
// ❌ 错误:直接停止接收
state <= S_ERROR;
end
.
// 后果:Slave还在发数据,Master不接收,协议违反
// ❌ 错误2:忽略RRESP,假设数据正确
assign data_valid = rvalid && rready;
// ❌ 没有检查rresp,错误数据被当成有效数据
正确做法(核心流程)
verilog
// ✅ 继续接收所有beats,但标记错误,丢弃本次突发所有数据
error_flag <= (rresp != 2'b00) ? 1'b1 : error_flag;
// 数据有效标志只在无错误时拉高
data_valid <= rvalid && rready && !error_flag;
// 突发结束后保持error_flag供上层重试
突发错误处理规则:
| 规则 | 说明 |
|---|---|
| 必须完成突发 | 即使中间出错,也要接收完所有Beats |
| 检查RRESP | 每个Beat都要检查,标记错误 |
| 丢弃错误数据 | 如果突发中有错误,整个突发数据无效 |
| 重试机制 | 出错后可以重发AR请求(需要上层协议支持) |
⚠️ 重要注意事项
注意事项一:AXI4和AXI3的突发长度不同
| 协议 | 最大突发长度 |
|---|---|
| AXI3 | 16次(AxLEN=0~15) |
| AXI4 | 256次(AxLEN=0~255) |
如果你的代码要兼容AXI3,必须把突发拆成多个16次的小突发。
注意事项二:突发传输的地址对齐要求
| 突发模式 | 地址对齐要求 |
|---|---|
| FIXED | 无要求 |
| INCR | 起始地址必须按AxSIZE字节对齐,否则Master报DECERR(很多DMA地址错位根源) |
| WRAP | 必须对齐到WRAP边界(突发长度×传输大小) |
💡 INCR地址对齐:AXI协议强制要求INCR/WRAP起始地址必须按AxSIZE对应字节对齐,否则Master直接返回DECERR。
注意事项三:ARSIZE/AWSIZE的含义
| AxSIZE值 | 每次传输字节数 |
|---|---|
| 000 | 1 byte |
| 001 | 2 bytes |
| 010 | 4 bytes |
| 011 | 8 bytes |
| 100 | 16 bytes |
| 101 | 32 bytes |
| 110 | 64 bytes |
| 111 | 128 bytes |
💡 重要:AxSIZE ≠ 数据位宽!AxSIZE表示每次传输的字节数,实际数据位宽由WDATA/RDATA决定。同一AXI接口可通过AxSIZE配置单拍1/2/4/8Byte窄传输,这是窄传输的核心来源。
常见搭配:
-
32bit接口 → AxSIZE=010(4 bytes)
-
64bit接口 → AxSIZE=011(8 bytes)
-
128bit接口 → AxSIZE=100(16 bytes)
注意事项四:Cache属性(ARCACHE/AWCACHE)
| Cache值 | 含义 | 典型场景 |
|---|---|---|
| 0000 | Device Non-bufferable | 外设寄存器 |
| 0011 | Normal Non-cacheable | 普通内存 |
| 1011 | Write-Back, Read-Allocate | DDR内存 |
| 0111 | Write-Through | 需要一致性的内存 |
建议:访问DDR用1011或0011,访问外设寄存器用0000。
注意事项五:调试突发传输用ILA抓完整序列
verilog
// ✅ ILA配置:抓取完整突发序列
ila_burst u_ila (
.clk(aclk),
.probe0(arvalid),
.probe1(arready),
.probe2(araddr),
.probe3(arlen),
.probe4(arburst),
.probe5(rvalid),
.probe6(rready),
.probe7(rdata),
.probe8(rresp),
.probe9(rlast),
.probe10(beat_cnt) // 内部计数器
);
// 触发条件:arvalid上升沿,抓取整个突发的完整波形
常见问题 FAQ
Q1:突发传输中途可以取消吗?
不能。AXI4协议规定,突发一旦开始必须完成。如果必须取消,Master只能设置超时忽略后续数据,上层协议重发请求。
Q2:如何知道一次突发传输了多少数据?
用arlen+1计算。突发长度 = arlen + 1。更可靠的方法是:自己维护计数器,每个rvalid+rready时记录Beat数。
Q3:多个突发可以同时发出吗?
可以,AXI4支持乱序。Master可以连续发出多个AR请求,Slave可以按任意顺序返回R数据(用RID标识)。如果设计不支持乱序,可以等上一个突发完成再发下一个,或用AXI Interconnect的reorder_depth=0禁用乱序。
Q4:FIXED突发模式什么场景用?
FIFO读写(地址固定,数据流式传输)、外设寄存器轮询(每次读同一地址)、DMA到固定地址。FIXED模式下,地址始终等于起始地址,不递增。
Q5:突发传输的性能优势有多大?
| 对比项 | 单次传输 | 16次突发 | 256次突发 |
|---|---|---|---|
| 地址开销 | 每次都有AR | 1次AR | 1次AR |
| 效率 | ~50% | ~90% | ~98% |
| 适用场景 | 零散访问 | 小块数据 | 大块DMA |
建议:超过16次传输就用突发模式。
Q6:为什么我的突发传输实际速度远低于理论值?
| 原因 | 检查方法 |
|---|---|
| 突发长度太短 | 检查arlen是否充分利用 |
| WSTRB导致窄传输 | 检查wstrb是否全1 |
| Interconnect仲裁延迟 | 检查是否有其他Master抢占 |
| Slave响应慢 | 检查rready/wready延迟 |
总结
| 痛苦点 | 根因 | 解法 |
|---|---|---|
| 突发长度算错 | AxLEN=实际次数-1 | 统一用 arlen = beats - 1 |
| WRAP传输出错 | 边界计算错误/地址不对齐 | 理解 WRAP边界 = 突发长度 × 传输大小 |
| 窄传输字节错位 | WSTRB没处理 | 根据WSTRB逐字节写入 |
| 突发中途出错 | 数据残缺/协议违反 | 继续完成突发,丢弃错误数据 |
AXI4-Full的精髓在于突发------算对长度、理解WRAP、处理窄传输、正确应对错误。
关注我,AXI协议实战专题持续更新。FPGA定制开发、项目调试、IP定制,开发服务可私。