4、AXI突发传输:FIXED/INCR/WRAP突发类型、地址计算、对齐规则

突发传输,说白了就是一次握手传一批数据。你想想看,如果每次只传一个数据就要走一遍地址握手,那效率太低了。AXI 的突发机制就是为了解决这个问题——你只需要给一个起始地址,剩下的地址由总线自己算。

我个人习惯把突发传输比作「批量发货」。地址是门牌号,数据是包裹。FIXED 是把所有包裹堆在同一个门口,INCR 是挨家挨户送,WRAP 是绕一圈回来继续送。嗯,咱们一个一个拆开看。

4.1 三种突发类型

AXI 协议定义了三种突发类型,由 ARBURST / AWBURST 信号控制,2'b00 到 2'b11 四个编码,其中 2'b10 是保留值。

BURST[1:0] 类型 地址计算方式 典型场景
2'b00 FIXED 地址不变,所有数据访问同一地址 状态寄存器轮询、FIFO 操作
2'b01 INCR 地址递增,步长 = 数据总线宽度 / 8 连续内存拷贝、DMA 搬运
2'b10 WRAP 地址递增到边界后回绕 Cache Line 填充、临界区数据
2'b11 保留 — —

4.2 FIXED 突发——地址焊死了

FIXED 模式下,每一次数据传输都使用相同的地址。我在项目中遇到过用它来读一个状态寄存器——外设的状态位只在一个地址上更新,你连续读 4 次,拿到的可能是 4 个不同时刻的快照。

地址计算:

AddressN = Start_Address

所有 beat 的地址都一样,不递增。

但要注意,FIXED 模式对从设备有要求。从设备必须能处理同一地址上的连续访问,比如 FIFO 的读指针会自动递增,或者寄存器本身支持多次读取。如果从设备不支持,数据可能会乱掉。

我曾经踩过的坑: 用 FIXED 突发去读一个普通 SRAM 的同一个地址,结果从设备返回的数据全是第一次读的值。因为 SRAM 的读操作不会自动更新数据,你读多少次都是同一个值。后来我才意识到,FIXED 只适合 FIFO 或状态寄存器这类「每次读都有新内容」的场景。

4.3 INCR 突发——最常用的递增模式

INCR 是工作中用得最多的突发类型。地址从起始地址开始,每次递增一个固定的步长。步长等于数据总线宽度除以 8,也就是一个 beat 的字节数。

地址计算公式:

Address_N = Start_Address + N × (Data_Bus_Width / 8)

举例:
- 32 位总线(4 字节),起始地址 0x1000
- Beat 0: 0x1000
- Beat 1: 0x1004
- Beat 2: 0x1008
- Beat 3: 0x100C

INCR 模式没有地址边界限制,可以跨 4KB 边界。但说实话,我建议你尽量不要让突发跨 4KB 边界。为什么?因为很多从设备的地址解码器是按 4KB 页来做的,跨页访问可能导致额外的延迟甚至错误。

我的习惯: 设计 DMA 控制器时,我会把最大突发长度限制在不超过 16 beat,并且保证起始地址对齐到 16 字节边界。这样既利用了突发的高效,又避免了跨页的麻烦。

4.4 WRAP 突发——绕圈子的艺术

WRAP 模式是 INCR 的一个变种。地址递增到某个边界后,会回绕到起始地址。这个边界由突发长度和地址对齐共同决定。

回绕边界计算:

Wrap_Boundary = (Start_Address / (Burst_Length × Data_Width)) × (Burst_Length × Data_Width)

举例:
- 32 位总线,突发长度 4,起始地址 0x100C
- 总数据量 = 4 × 4 = 16 字节
- 回绕边界 = (0x100C / 16) × 16 = 0x1000
- Beat 0: 0x100C
- Beat 1: 0x1010  → 到达边界 0x1010,回绕
- Beat 2: 0x1000
- Beat 3: 0x1004

WRAP 模式在 Cache 设计中特别常见。比如 Cache Line 的大小是 16 字节,你从中间某个偏移开始读,读完边界后自动绕回开头,正好填满整个 Line。

关键点: WRAP 突发的起始地址必须对齐到总数据量的边界。比如 4 beat × 4 字节 = 16 字节,起始地址必须是 16 的倍数。如果不对齐,从设备的行为是未定义的——说白了就是可能出错。

4.5 地址对齐规则

地址对齐是 AXI 协议里最容易出问题的地方。我见过不少工程师在这里翻车。

核心规则只有一条: 起始地址必须对齐到数据总线宽度的字节数。

数据总线宽度 对齐要求 起始地址低 2 位
32 位(4 字节) 4 字节对齐 2'b00
64 位(8 字节) 8 字节对齐 3'b000
128 位(16 字节) 16 字节对齐 4'b0000

如果起始地址没有对齐,你需要用 WSTRB 信号来标记哪些字节是有效的。比如 32 位总线,起始地址 0x1001,那么第一个 beat 只有字节 1、2、3 有效,字节 0 被跳过。

我曾经踩过的坑: 设计一个 AXI 转 AHB 的桥接器,从设备要求所有突发必须对齐。我忘了检查起始地址的对齐情况,结果在地址 0x1002 上发了一个 4 beat 的 INCR 突发,从设备直接返回 SLVERR。后来我加了一个地址对齐检查逻辑,不对齐的突发拆成多个对齐的突发,问题才解决。

4.6 三种突发对比总结

特性 FIXED INCR WRAP
地址变化 不变 递增 递增 + 回绕
跨 4KB 边界 不涉及 允许(不推荐) 不允许
起始地址对齐 无特殊要求 建议对齐 必须对齐到总数据量
典型应用 FIFO、寄存器 内存搬运 Cache Line
地址计算复杂度 低 中 高

4.7 突发传输流程图

下面这张图展示了三种突发类型的地址变化轨迹,方便你对比理解。

AXI 三种突发类型地址变化对比 AXI 三种突发类型地址变化对比 FIXED 0x1000 0x1000 0x1000 0x1000 INCR 0x1000 0x1004 0x1008 0x100C WRAP 0x100C 0x1010 → 回绕 0x1000 0x1004 说明: • FIXED:所有 beat 地址相同,适合 FIFO/寄存器 • INCR:地址线性递增,适合连续内存访问 • WRAP:地址递增到边界后回绕,适合 Cache Line 填充

4.8 实战建议

说了这么多理论,最后给几条实战建议:

  • 优先用 INCR:90% 的场景用 INCR 就够了,简单可靠。
  • WRAP 只用在 Cache 场景:别在普通内存访问中用 WRAP,容易把自己绕晕。
  • FIXED 要谨慎:确认从设备支持同一地址的连续访问,否则数据会重复。
  • 地址对齐检查不能省:在 RTL 代码里加一个断言,起始地址不对齐就报错,省得仿真时查半天。
  • 突发长度别太大:超过 16 beat 的突发,很多从设备不支持。我一般控制在 4~8 beat。

我的小技巧: 写 AXI 验证环境时,我会随机生成 FIXED、INCR、WRAP 三种突发,然后交叉验证地址计算是否正确。用 SystemVerilog 的随机约束,几分钟就能跑几百个 case,比手动检查靠谱多了。