3、AXI传输特性:突发传输、乱序传输、交织传输与原子访问
AXI协议之所以能成为高性能总线的事实标准,核心就在于它对传输行为的精细控制。说白了,它不光告诉你怎么传数据,还允许你“插队”、“乱序处理”,甚至“锁住”某个地址不让别人碰。
我刚开始接触AXI时,总觉得这些特性是锦上添花。直到有一次调试一个多主控的DDR控制器,数据死活对不上,才发现自己对“乱序”的理解太肤浅了。嗯,这一章咱们就把这几个核心特性掰开揉碎了讲清楚。
3.1 突发传输(Burst)
突发传输是AXI最基础的加速手段。它的思路很简单:你告诉总线“我要从地址A开始,连续读/写N个数据”,总线就一气呵成地帮你搞定,不用每个数据都发一遍地址。
核心三要素:
- 突发长度(Burst Length):一次突发传输的数据个数。1~16(对于INCR类型,可以到256)。
- 突发大小(Burst Size):每个数据的字节数。1、2、4、8、16、32、64、128字节。
- 突发类型(Burst Type):FIXED(固定地址)、INCR(递增地址)、WRAP(回环地址)。
我个人习惯把突发类型记成三种场景:
- FIXED:所有数据都打在同一个地址上。典型场景是写FIFO或状态寄存器。我见过有人拿FIXED去读DDR,结果数据全是同一个地址的内容,排查了半天才发现类型配错了。
- INCR:地址依次递增。最常用,比如从内存读一段连续的数据。
- WRAP:地址递增到边界后回环到起始地址。专门为Cache Line设计,比如64字节的Cache Line,地址对齐到64字节边界,读到边界就自动折回来。
这里有个容易踩的坑——地址对齐。突发传输要求起始地址必须对齐到突发大小。比如你设Burst Size=8字节,起始地址必须是8的倍数。我曾经在项目中用了一个非对齐的起始地址,结果仿真波形里地址乱跳,数据也错位了。后来养成习惯:每次配突发参数前,先算一遍地址对齐。
实战小技巧:
计算突发传输的总字节数:Burst Length × Burst Size。这个值不能超过4KB边界。为什么?因为AXI协议规定一次突发不能跨4KB边界。这是为了简化Slave的地址解码逻辑。
3.2 乱序传输(Out-of-Order)
乱序传输是AXI提升总线利用率的关键。它允许Slave不按Master发请求的顺序返回数据。
你想想看:Master先发了读请求A(地址在DDR),又发了读请求B(地址在片上SRAM)。如果必须按顺序返回,那Master得干等DDR的几百个周期,SRAM的数据再快也得排队。有了乱序,SRAM的数据可以先回来,Master先处理它,效率一下子就上去了。
乱序的实现靠的是ID标签。每个传输事务都带一个ID,Slave返回的数据必须带上相同的ID。Master根据ID来匹配请求和响应。
| 特性 | 说明 |
|---|---|
| ID宽度 | AXI3支持4位ID(最多16个未完成事务),AXI4扩展到8位(最多256个) |
| 乱序深度 | Slave可以同时处理多个不同ID的事务,并按任意顺序返回 |
| 同ID顺序 | 相同ID的事务必须按顺序返回——这是协议硬性规定 |
我曾经踩过的坑:
在设计一个DMA控制器时,我给所有传输都用了同一个ID。结果Slave虽然支持乱序,但看到同ID就老老实实按顺序处理了,性能完全没发挥出来。后来改成每个通道分配独立ID,乱序效果立竿见影。
记住:乱序的前提是ID不同。想充分利用乱序,就得合理分配ID资源。
3.3 交织传输(Interleaving)
交织传输比乱序更进一步。乱序只是允许响应乱序回来,但交织允许数据在传输过程中被拆散穿插。
举个例子:Master发了两笔写传输,事务1有4个数据,事务2有4个数据。在交织模式下,Slave可以先收事务1的WDATA[0],再收事务2的WDATA[0],再收事务1的WDATA[1]……数据在总线上是交错传输的。
交织传输在AXI3中是可选特性,但到了AXI4被完全移除了。为什么?因为实际设计中,交织带来的硬件复杂度远大于收益。Slave需要为每个交织的事务维护独立的写数据缓冲和状态机,面积和时序压力都很大。
AXI3 vs AXI4 交织对比:
- AXI3:支持写交织(Write Interleaving),通过WID信号区分数据属于哪个事务。
- AXI4:不再支持写交织,WID信号被移除。所有写数据必须按顺序发送。
- 读交织:两个版本都不支持读交织。读数据通道本来就只有一个,不存在交织问题。
我个人建议:如果你在做AXI3的老项目,尽量别用交织。除非你有非常充分的理由(比如某个老IP强制要求),否则老老实实按顺序发数据,省下来的逻辑资源干点别的不好吗?
3.4 原子访问(Atomic Access)
原子访问解决的是多主控环境下的竞争问题。两个Master同时读写同一个地址,如果没有保护机制,数据就乱套了。
AXI提供了两种原子访问机制:
- 独占访问(Exclusive Access):Master先读一个地址(打上独占标记),然后写同一个地址。如果这期间没有其他Master写这个地址,写操作成功;否则写操作失败,Master需要重试。
- 锁定访问(Locked Access):Master锁定总线,不允许其他Master发起传输,直到锁定事务完成。这种方式比较粗暴,会降低总线利用率。
| 特性 | 独占访问 | 锁定访问 |
|---|---|---|
| 总线占用 | 不锁定总线,其他Master可以继续传输 | 锁定总线,其他Master必须等待 |
| 实现复杂度 | 需要Slave支持独占监视器 | 相对简单,但影响性能 |
| 适用场景 | 信号量、互斥锁 | 需要严格连续操作的场景 |
| AXI4支持 | 支持 | 不再推荐使用(AXI4中ARLOCK/SLOCK信号被简化) |
我在项目中用过独占访问来实现多核之间的信号量。具体做法是:每个核在访问共享资源前,先执行一次独占读;如果读到的值是0(表示资源空闲),就执行独占写把值改成1;如果写成功,说明拿到了锁。整个过程是原子的,不会出现两个核同时拿到锁的情况。
独占访问的硬件要求:
Slave必须实现一个独占监视器(Exclusive Monitor),记录每个地址的独占状态。监视器通常是一个硬件状态机,维护一个地址-状态表。当Master发起独占读时,监视器标记该地址为“已独占”;当其他Master写这个地址时,监视器清除独占标记。独占写时,监视器检查标记是否存在,存在则写成功,不存在则写失败。
3.5 四种特性对比总结
这四种特性从不同维度提升了总线的灵活性和效率。我画了一张图,帮你理清它们的关系:
从图中可以看出,这四种特性各有侧重:突发传输解决的是“怎么传得快”,乱序和交织解决的是“怎么传得灵活”,原子访问解决的是“怎么传得安全”。
在实际项目中,我通常这样选择:
- 数据流场景(DMA、显示控制器):优先用突发传输,配合合理的ID分配实现乱序。
- 控制面场景(寄存器读写):用单次传输,必要时加独占访问保护。
- 多核共享数据:原子访问是必须的,否则数据一致性会出大问题。
重要提醒:
不要为了用特性而用特性。每个特性都有代价:突发传输需要地址对齐检查,乱序需要ID管理和重排序缓冲,原子访问需要独占监视器。在设计中,够用就好。我见过一个项目,工程师为了追求极致性能,把所有特性全开了,结果综合出来的面积比预期大了30%,时序也收不拢。后来砍掉不必要的交织和深度乱序,问题才解决。