AXI4 写数据交织:AW 与 W 的配对、同 ID 保序与 WID 取消始末
更新时间:2026-09-25。本文是「芯片设计 · 进阶层」接口主题簇文章。前置阅读 AXI4 完整协议 讲清了五通道与突发,AXI4 outstanding 与 ID 保序 讲清了多笔在途与乱序规则,valid/ready 握手 讲清了单拍转移。本篇专门解决写通路最容易被忽视的一组问题:地址发出去了,数据凭什么知道属于谁?
本文要回答的问题
- AW 通道和 W 通道是两条独立的流,slave 收到一串写数据时怎么知道每拍属于哪笔事务?
- 同 ID 的写事务保序规则具体是什么?不同 ID 真的可以乱序吗?
- AXI3 的 WID 信号为什么在 AXI4 里被删掉?为什么说 AXI4 禁止了一切写数据交织?
- slave 和 interconnect 各用什么 FIFO 结构完成 AW/W 配对?经典写死锁怎么形成、怎么破?
一、写地址与写数据:天生分离的两条流
AXI 的一次写事务拆在两个通道上:AW 通道携带 AWID、地址、突发长度与大小等控制信息,W 通道只携带 WDATA、WSTRB 和 WLAST。注意一个关键事实:AXI4 的 W 通道上没有任何 ID 信号,也没有地址。 这意味着 W 通道上流过的每一拍数据,自己并不声明归属,归属关系完全靠它在流中的位置推断。
更麻烦的是,AW 和 W 是各自独立的 valid/ready 流,协议不规定谁先到达 slave。同一个 master 可以先把两三笔 AW 全发出去再吐数据,也可以数据先到、地址后到,中间还允许插入任意空闲拍。
正因为「无 ID + 到达顺序不定」,协议必须用一条额外的顺序规则把两条流缝起来,否则 slave 根本无法把拍数据归位到地址。这条规则就是下一节的写数据排序模型。
可以先用一个直觉测试理解这条规则的分量:假设 master 连发两笔写,第一笔 4 beat 写往地址 A、第二笔 2 beat 写往地址 B,slave 依次收到 6 个 W beat。没有 WID,slave 唯一能依赖的信息就是「前 4 拍属于 A、后 2 拍属于 B」,再用每笔各自的 WLAST 校验边界。只要 master 在拍级破坏这个顺序——哪怕只是把 B 的一拍插到中间——slave 没有任何手段察觉,数据会静默地写错地址。协议因此干脆在源头禁止穿插,把「顺序即归属」定为强约束。也可以反过来说:正是因为 W 通道不带身份信息,这条拍级顺序不是性能建议,而是正确性的唯一来源,master、slave、互连三方都没有违约的余地。
二、写数据排序模型:同 ID 保序,全部交织被禁
AXI 协议对写数据有两层顺序约束,一层比一层强。
第一层,事务级:同一 ID 的写事务必须按发起顺序完成。 master 用同一个 AWID 连发两笔写,slave 必须先完成第一笔(先看到它的地址、先落它的数据、先回它的 BRESP),再完成第二笔。不同 AWID 的写事务之间没有顺序保证,理论上谁先完成都行——这是写通路保留乱序能力的唯一口子。
第二层,拍级:master 必须按 AW 的发出顺序产生 W 数据,AXI4 不允许任何写数据交织。 也就是说,如果 master 按顺序发出 AW(id=0)、AW(id=1),那么 W 通道上必须把 id=0 这笔的所有 beat 连续送到 WLAST,之后才允许出现 id=1 的 beat。即使两笔 ID 不同,也不能「0 的一拍、1 的一拍」穿插着发。
| 拍序(W 通道) | AXI3(有 WID) | AXI4(无 WID) |
|---|---|---|
| beat 1 | id=0 的 data | id=0 的 data |
| beat 2 | id=1 的 data(穿插,合法) | id=0 的 data |
| beat 3 | id=0 的 WLAST(穿插,合法) | id=0 的 WLAST |
| beat 4 | id=1 的 WLAST | id=1 的 data |
| beat 5 | — | id=1 的 WLAST |
注意区分读侧:AXI4 同时禁止了读数据交织,但仍允许不同 ID 的读事务乱序完成;写侧的限制更彻底——事务级乱序还在(不同 AWID 可先后完成),拍级交织在 AXI4 中一律非法。很多材料说「AXI 写数据本来就不能 interleave」,这在 AXI4 语境下成立,但 AXI3 确实靠 WID 放过行,追设计时不能混为一谈。
这里有个常被追问的细节:W 数据都按 AW 序成段发送了,不同 ID 怎么可能乱序完成?答案在互连侧。两笔写命中不同 slave 时,interconnect 虽然按序收下 W 段,却可以把它们转发到各自的目标端口,慢目标(比如正在刷新的 DDR)晚完成、快目标(比如 SRAM)先回 BRESP,于是 B 通道上 BID=1 的响应可能早于 BID=0。顺序约束只绑定「同 ID」,从不绑定 B 通道的全局次序;接收方只能凭 BID 对号,不能假设响应先后。而在单个简单 slave 内部,由于配对和写入都按队列走,不同 ID 实际也往往按序完成——乱序能力是为多目标互连保留的,不要求每个 slave 都实现。
三、为什么删掉 WID:收益太小,代价全在互连
WID 的唯一作用,是让 W 通道上的每一拍自己声明归属,于是不同 ID 的写数据理论上可以在总线上穿插发送。这个能力看似灵活,实际工程中几乎没有 master 用它,却把复杂度全部压给了 slave 和 interconnect。
具体代价有三:
- 择路粒度从事务退化成 beat。 两笔写若命中不同 slave,interconnect 要在 W 通道上每拍查 WID、切换数据 MUX,同时向两个 slave 端口供数;没有 WID 后,一笔写的整段数据只走一个方向。
- slave 要为并发写备多套上下文。 既然 id=0、id=1 的 beat 可能交替到来,slave 就得同时维护多组「地址 + 已收拍数」状态并按 WID 分发数据;AXI4 下 slave 只需把 W 流依次归给最早待配对的 AW。
- 时序与验证成本高。 穿插组合空间大、死锁面大,而带宽收益微乎其微——master 侧攒齐一笔数据成段发送几乎没有损失。
ARM 在 AXI4 中做的取舍很清楚:用一个没人真正需要的拍级自由度,换全链路 MUX、缓冲和排序逻辑的大幅简化。取消 WID 之后,「W 数据按 AW 序成段到达」成为隐含契约,配对问题就变成了纯粹的 FIFO 组织问题。
在 AXI3 老系统里,这个自由度还有配套的 AWINTERLEAVE 深度概念:slave 要声明自己能同时接受多少笔不同 ID 的穿插写,深度为 1 就等于不接受交织。现实中绝大多数 slave 把该深度实现成 1,交织能力在设备端就已经名存实亡,AXI4 只是把公认的事实写进了协议。今天做 AXI3/AXI4 混用设计时要注意:AXI3 master 的 WID 输出在进入 AXI4 互连前必须被「漂白」——转换器要校验 WID 与 AW 序一致,若出现真穿插只能先缓存重排成段,不能直接把 WID 剪掉放行,否则下游会静默错位。
四、Slave 怎么配对:AW 队列加 W 队列,顺序对号
一个守规矩的 AXI4 slave,配对逻辑可以浓缩成一句话:维护一个按到达顺序排列的 AW 队列,把 W 流的 beat 依次归给队首那笔事务,数到 WLAST 就弹出队首。
但由于 AW、W 到达先后不定,两个方向都要能独立缓存,常见组织如下。
实现要点:
- 用
aw_pending、w_pending两组独立标志记录「当前这笔是地址先到还是数据先到」,哪边先到就先缓存哪边,绝不强制 AW 先于 W。 - beat 计数由 AWLEN 决定,收到
WLAST时应恰好数满;对不上说明上游违约,可挂错误中断而非默默错位。 - 只有当一笔写的地址和全部数据都到齐后,才向存储体执行一次完整写入,并在 B 通道按该笔的
AWID回BID/BRESP。 - 同 ID 保序在此自然成立:队首不完成,同 ID 的后一笔即使已在队列里也拿不到存储体写权限。
这种「各自可缓存、按序配对」的简单 slave 结构,在 AXI4-Lite 从机设计 里有最小实现范例;加上多笔突发和在途表,就是完整 AXI4 slave 的写法。
展开看配对控制的状态机,核心只有一个递减计数和两个边沿动作:AW 握手时把 AWLEN + 1 载入当前事务的 beat 计数器;每完成一次 W 握手就减一;减到 0 的那拍要求 WLAST 同时为高,随即把队首弹出、载入下一笔 AW 的长度。AW 队列为空时 WREADY 应拉低,无主的数据不可收;W 段未走完时队首不可弹。若还要支持多笔写在 slave 内并发落盘,则把「计数器 + 缓存」复制成多张表,AW 入队时分配表项、WLAST 时释放,但写存储体的仲裁仍按 AW 队列序放行同 ID 表项,这与 AXI4 outstanding 与 ID 保序 里的记分板是同一套结构。
五、Interconnect 的写死锁:环形等待是怎么长出来的
单 slave、单 master 时配对很简单;一旦多 master 经 interconnect 访问多 slave,AW、W 在互连上分别仲裁,死锁环就出现了。
环的逻辑链是这样的:master0 的 AW 已经被仲裁到 slave A,但它的 W 数据因为 W 通道缓冲被占住发不出去;master1 同样「AW 已到、W 卡住」;下游只收齐一对才排空、上游只看到排空才继续推进,于是谁也动不了。根因还是那两条独立流被跨 master 重新组合后,产生了跨事务的资源依赖。
标准破法是在 interconnect 内部把 AW 和 W 重新绑成一对再提交,常用两种策略:
- 数据先缓存、地址后放行(store-then-issue)。 master 端口先把整笔 W 数据收进本地写缓冲,再向 slave 端口发出 AW,保证 AW 一出、数据立刻能跟上,slave 端口上永远不会出现「裸 AW 等数据」。
- 按内部 tag 重排,成对提交。 为每笔在途写分配内部 tag,AW 和 W 分别带 tag 进入各自缓冲,slave 端口的仲裁器只在某笔的两半都就绪时,才把这笔的 AW 紧接其 W 数据一起送下去,顺序由该 slave 端口的提交 FIFO 统一决定。
还有一种更隐蔽的死锁发生在 B 通道:如果 slave 必须回 BRESP 才能释放内部写表项,而 master 一直不拉 BREADY、同时又持续发新的 AW/W,slave 的表项和缓冲会被「已完成但响应未取走」的事务占满,回头拒收新数据,master 却以为是普通背压继续等。规避办法是让 B 通道拥有独立于 AW/W 的缓冲深度,并要求 master 即使在满负荷发送时也持续服务 B 通道握手,别让响应通道饿死。
配套纪律还有几条:给在途表预留至少一个可排空表项,避免资源全占满后没有事务能完成(经典存储转发死锁条件);任何出口(正常完成、DECERR 早退、复位)都必须释放 tag 和缓冲,否则表项泄漏会让总线跑一段时间后挂死;VALID 一旦举起必须自保持,不能组合地依赖 READY。多从机仲裁与端口组织可参考 AXI Crossbar 与 Interconnect,FIFO 深度估算可参考 FIFO 深度设计。
六、设计与验证时的检查清单
把本文的规则落成可执行的判据,设计和验证各看一组。
| 角色 | 检查项 |
|---|---|
| Master RTL | W beat 顺序与 AW 发出顺序一致;同 AWID 不抢先;每笔 beat 数与 AWLEN 相符、WLAST 不早不晚 |
| Slave RTL | AW/W 任一先到都能独立缓存;按队列序配对;BID 与 AWID 一致;异常路径释放上下文 |
| Interconnect | slave 端口成对提交 AW/W;按 tag 重排;预留可排空表项;不允许 VALID 组合依赖 READY |
| 验证用例 | W 先于 AW、多笔 AW 攒射、不同 ID 背靠背、背压中途拉低 READY、DECERR 早退后再发新写 |
验证写数据交织问题时,最有效的手法是在 monitor 里独立维护一份 AW 队列模型:每来一笔 AW 入队,每来一个 W beat 就断言它属于队首事务,遇 WLAST 弹队首。任何错位、beat 数不符、WLAST 缺失都会立刻被抓住,不必依赖人工看波形。同一套判据还可以综合成 SVA 断言放进 RTL 仿真,例如「W 握手时 AW 队列不得为空」「计数器归零当拍 WLAST 必为高」,让违约在第一拍暴露。跨桥接还要盯 ID 位宽:经窄 ID 桥(如 AXI 转 APB)后高位 ID 被截断,回来的 BID 会与发出的对不上,桥接器必须保证 ID 透传,或 master 只使用桥支持的 ID 位宽。
最后提醒一个回归统计层面的坑:交织/保序类 bug 往往只在特定 seed、特定背压组合下出现,单次冒烟通过完全不能证明没问题。回归配置里要把「AW 先到/W 先到、READY 拉低位置、多 ID 背靠背」做成可随机组合的场景维度,并保证失败 seed 被原样记录、可一键重放。把随机稳定性纳入用例基类(每次用例启动打印 seed、失败时回贴重放命令),这类低概率错位才不会在项目后期变成「偶发挂死、查无实据」的遗留问题。
相关与延伸
协议基础见 AXI4 完整协议;在途与乱序全景见 AXI4 outstanding 与 ID 保序;握手机制见 valid/ready 握手;最小从机范例见 AXI4-Lite 从机设计;互连组织见 AXI Crossbar 与 Interconnect;加拍切时序见 AXI 寄存器切片;同 ID 约束下的原子语义见 AXI4 独占访问。
总结
AXI4 写数据的归属全靠顺序:W 通道无 ID,master 必须按 AW 发出顺序把每笔写的 beat 成段送到 WLAST,同 ID 事务严格保序、拍级交织被一律禁止——这正是 AXI4 删掉 WID 换来的简化;slave 用「AW FIFO 加 W FIFO、队首配对」收数据,interconnect 则必须按 tag 缓存、两半齐全后成对提交,才能切断 AW/W 跨事务等待的死锁环。