AXI4 事务生命周期:写事务 AW→W→B、读事务 AR→R 的通道推进与死锁防护
更新时间:2026-09-25。本文是「芯片验证 · 进阶层」接口主题簇文章。前置阅读 AXI4 完整协议 建立五通道与 burst 的整体概念,valid/ready 握手 讲清单拍转移规则,AXI4 outstanding 与 ID 保序 讲多笔在途与乱序;本篇把镜头拉到「一笔从事务发起到彻底收尾」的完整生命周期,逐通道、逐拍地推进,并指出每一步在 RTL 里占用和释放了什么资源。
本文要回答的问题
- 一笔写事务在 AW、W、B 三个通道上按什么顺序推进?AW 和 W 谁先到合法?
- 一笔读事务的 AR 与 R 之间有哪些必须满足的依赖?
- valid/ready 握手中,VALID 先举、READY 先举、同拍举起分别意味着什么?
- 多笔事务同时在途时,计数器和上下文表项在哪一拍分配、在哪一拍释放?
- 什么条件下整条总线会死锁(典型如 B/R 通道无人接收),RTL 上怎么防护?
一、把一笔事务看清楚:五通道与两条单向链路
AXI4 把一次读写拆成五个独立通道,每个通道都是一组单向的 valid/ready 流。写事务占用三条通道(AW 地址、W 数据、B 响应),读事务占用两条通道(AR 地址、R 数据兼响应)。
| 通道 | 方向 | 关键载荷 | 在一笔事务中的拍数 |
|---|---|---|---|
| AW | M→S | AWADDR、AWLEN、AWSIZE、AWBURST、AWID、AWPROT | 恒为 1 拍 |
| W | M→S | WDATA、WSTRB、WLAST、(AXI3 有 WID) | AWLEN+1 拍 |
| B | S→M | BRESP、BID | 恒为 1 拍 |
| AR | M→S | ARADDR、ARLEN、ARSIZE、ARBURST、ARID | 恒为 1 拍 |
| R | S→M | RDATA、RRESP、RLAST、RID | ARLEN+1 拍 |
读事务的响应不单独占通道:每拍 R 数据上都带 RRESP,正常 burst 各拍为 OKAY,最后一拍 RVALID 那拍 RLAST 同时为 1。写事务则相反:数据通道上没有响应,必须等全部数据收完后由 B 通道回单独的一拍 BRESP。
这种拆分不是形式主义,它直接决定了资源占用方式:地址通道恒为一拍,意味着「发请求」很便宜;数据通道多拍,意味着事务的大部分时间花在数据搬运上;响应单独收尾,则意味着从机必须为每笔已接收但未收尾的事务保存上下文。记住「一拍地址、多拍数据、一拍收尾」这个节奏,后面所有的计数与死锁分析都建立在它之上。
二、valid/ready:每一拍推进的唯一规则
五个通道使用同一套握手规则:发送方举 VALID 表示「本拍有效载荷已稳定」,接收方举 READY 表示「本拍可以接收」,两者同时为高的那一拍发生一次转移(transfer),下一拍发送方才允许更换或撤销内容。

由此推出三条实现纪律:
- VALID 一旦举起必须保持,直到握手完成。 不能因为 READY 迟迟不来就撤下 VALID,更不能让 VALID 组合地依赖 READY——后者在仲裁环路里可能形成组合环。
- READY 可以随时举起或撤下,也可以先于 VALID。 READY 本质是「我有一个空位」,对从机而言常见做法是 FIFO 非满时直接把 READY 接出来。
- 同拍举起完全合法。 验证平台必须覆盖 VALID-first、READY-first、same-cycle 三种时序,只测其中一种会漏掉大量握手指令交错的 bug。
还有两个容易在验证中失之毫厘的细节。其一,VALID 举起后,载荷必须在同一拍就稳定,不能等看见 READY 才把地址或数据填上去——所以「VALID 依赖 READY、READY 又依赖 VALID 侧某个状态」的写法既是协议违规,也可能综合出组合环路。其二,握手成功的那一拍转移即告完成,接收方必须当拍采走内容;如果接收逻辑当拍用不了,正确做法是在其后接一拍锁存或 FIFO,而不是在握手拍上附加额外条件。
握手只解决「一拍」的转移;一笔 burst 有若干个这样的拍,而一笔事务的完整推进还需要通道之间的依赖规则,下面分写、分读展开。
三、写事务:AW→W→B 的三段推进
一笔长度为 3 拍的写事务,完整时序如下。注意 AW 只占一拍,W 连续 3 拍并在最后一拍拉起 WLAST,B 在全部数据收完后才回。

通道依赖只有一条是硬性的:B 必须在该笔事务全部 W beat(含 WLAST)被接收之后才能发出。 而 AW 与 W 之间没有法定先后——协议允许 W 数据先于 AW 到达。因此一个健壮的从机不能写成「收到 AW 才拉 WREADY」,而要用两个独立标志记录各自进度:

写通道上还有两个辅助信号值得放进同一张时序图理解。WSTRB 每个字节 lane 对应一位,为 0 的字节即使 WVALID 握手也不写入,常用于只改寄存器里个别字节的场景(窄传输或非对齐写);它和 WDATA 一样在 WVALID 期间保持稳定。BRESP 只有两比特四种取值:OKAY 表示正常成功,EXOKAY 用于独占访问,SLVERR 表示从机收到了请求但访问失败(如保护违规、FIFO 满),DECERR 表示地址译码不到任何从机——后两种是从机在「收齐数据后明确拒绝」,B 通道照发、事务照收尾,只是主机要据此决定重试还是报错。
工程上更常见的实现不是一个状态寄存器,而是 AW、W 各自接一个小 FIFO(或一组独立锁存),内部用同一份事务上下文配对:AW FIFO 给出地址和长度,W 侧数够 AWLEN+1 个 beat 即置 w_done。这样无论哪条路先到都不会丢数据,也不会因为等另一条路而僵住。还要注意多笔写连续发起时,AXI4 已删除 WID,写数据通道全局不允许交叉:第二笔事务的 beat 必须等第一笔的 WLAST 之后才能上通道,配对关系由 AW 发起顺序隐含。AXI4-Lite 从机的简化版(单笔、无 burst)可参考 AXI4-Lite 从机设计。
四、读事务:AR→R,地址一拍、数据连续回
读事务更短:AR 通道一拍交代地址和 burst 参数,从机准备好数据后在 R 通道连续返回 ARLEN+1 拍,最后一拍 RLAST=1。

两条依赖需要注意:
- R 的全部 beat 必须在 AR 握手之后。 没有 AR 上下文,从机无从知道长度与地址;这条依赖也界定了 outstanding 表项的生命周期。
- AXI4 禁止读数据交叉(no read interleaving)。 一笔事务的 R beat 必须连续发完,才能发另一笔(即使 RID 不同)。事务级仍允许乱序——先发起的慢读可以被后发起的快读整体超过,但接收方要按 RID 对号,详见 AXI4 outstanding 与 ID 保序。
把这条规则与时序图合起来看:主机在第 2 拍拿到 AR 握手后,无需等待第 6 拍的 RLAST,就可以立刻在第 3、4 拍继续发第二笔、第三笔 AR。于是从机端口上可能出现「地址已收三笔、数据一笔都没回」的状态,这正是合法且被鼓励的在途形态;唯一的限制是主机授信深度与从机表项深度必须匹配,超过对端容量的那笔 AR 会在 ARREADY=0 上自然等待。
读响应还有一个与写不同的细节:RRESP 是逐 beat 给出的,因此错误可以只标记在受影响的 beat 上(例如 burst 中途某拍越过有效地址范围,该拍 RRESP 为 DECERR,其余拍仍 OKAY)。主机不能只检查第一拍,应当对每个返回 beat 都采样响应。
RREADY 由主机给出。主机如果暂时不处理返回数据,最常见的做法是在接口处放接收 FIFO:只要 FIFO 非满,RREADY 常高,避免因为内部反压把从机长时间顶在半路。反过来,从机在数据没准备好时让 RVALID 保持为低即可,AR 握手到首个 R beat 之间的间隔就是访问延迟,可以是任意拍数——这段延迟对单笔事务是纯粹的等待,正是靠下一节的多笔在途把它填满的。
五、多笔在途:计数器与表项在哪拍分配、在哪拍释放
单拍握手规则保证每一拍不丢数据,但多笔事务同时在途时,还需要一层事务级资源计数:每笔在途事务占用一个上下文表项(存 ID、地址、剩余 beat 数),表项数量就是 outstanding 深度。
| 事件 | 动作 | 计数器变化 |
|---|---|---|
| AR 握手成功 | 分配读表项,存入地址/长度/ID | ar_outstanding +1 |
| 最后一个 R beat(RLAST)握手 | 读事务完成,释放表项 | ar_outstanding -1 |
| AW 握手成功 | 分配写表项(仅地址到达) | aw_outstanding +1 |
| WLAST 握手 | 数据收齐,发起写、准备 B | 表项仍占用(等 B) |
| B 握手成功 | 写事务彻底完成,释放表项 | aw_outstanding -1 |
关键点:写表项不能在 WLAST 时释放,因为 B 还没发、BID 还要从表项里取;只有 B 握手完成才算闭环。读表项则以 RLAST 握手为释放点。一个最小计数器写法如下:
logic [4:0] ar_outs; // 在途读事务数
wire ar_alloc = arvalid & arready;
wire r_dealloc = rvalid & rready & rlast;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n)
ar_outs <= '0;
else
ar_outs <= ar_outs + ar_alloc - r_dealloc;
end
// 反压:表项将满时不再接收新地址
assign arready = (ar_outs < MAX_OUTS);写侧同理,alloc 点在 AW 握手、dealloc 点在 B 握手。这里要特别澄清一个常见误解:写事务的生命周期跨 AW、W、B 三段,中间没有「半释放」。WLAST 只表示数据到齐,表项还要继续保存 ID 直到 B 握手;如果用两个计数器,还可以把写拆成「已发 AW 数」与「已收 B 数」分别统计,二者之差才是真正占用上下文的写事务数。验证环境里的参考模型(scoreboard)应完全照此记账:地址握手时入队、最终响应握手时出队,并在仿真结束时断言队列为空——任何残留表项都会被这条断言抓住。
多笔在途时还要保证一条纪律:每条退出路径都必须释放表项——正常完成、SLVERR/DECERR 早退、突发长度非法、复位清除,任何一条路径漏掉 dealloc,表项就永久泄漏,总线跑一段时间后必然挂死。此外表项里存的「剩余 beat 计数器」本身也是排查抓手:若一笔事务卡在固定剩余数不动,波形上直接就能看出是 WREADY/RREADY 长期为低,还是从机内部仲裁饥饿。
六、死锁:B/R 无人接收与 AW/W 错配
事务级死锁几乎都源于「通道能独立反压,而资源释放点在最后一条通道上」。最典型的场景是响应通道无人接收:主机因为内部缓冲满或状态机设计缺陷,长期不拉高 BREADY/RREADY,从机的收尾动作发不出去。
环路上每一步单独看都「合法」,合起来就是死锁。另一类经典死锁发生在 Interconnect 写通路:AW 与 W 是两条独立流,若从机强制「先 AW 后 W」而仲裁器把 W 通道先占给了另一主机,两边互相等待对方先腾出通道。对应的 RTL 防护清单如下:
- 响应必须永远能被排空。 主机侧 B/R 接收路径用独立 FIFO 解耦内部反压,任何状态下都不能长期拒收响应;FIFO 满度要纳入 outstanding 授信,而不是等满了才停发。
- AW、W 独立缓存,不强制先后。 从机和 Interconnect 都应允许任一条先到,用 aw_done/w_done 两个标志配对,切断跨通道等待。
- 授信留余量。 发起方 outstanding 上限小于整条通路表项总数,预留至少一个可排空槽位,避免「全部表项等解锁事务,而解锁事务无处可去」的拥塞死锁。
- VALID 不组合依赖 READY。 举起后自保持到握手,杜绝组合环与活锁。
- 异常路径同样释放资源。 DECERR、长度错误、复位清除都要归还表项并停掉对应 VALID;可用超时看门狗(计数器监视某表项停留拍数)兜底报警并强制回收。
- 下游排空优先于上游接收。 仲裁器在拥塞时优先服务「能释放表项」的方向(先发 B/R、先完成已在途写),再接收新地址,保证系统始终朝可排空方向推进。
现场排查时,这套机理给出了很固定的诊断路径:先看总线是否完全无握手,再看 BVALID/RVALID 是否长期举着而 READY 为低——若是,问题在主机接收侧;若 BVALID/RVALID 根本举不起来,则顺着表项查到从机内部,看它在等哪个条件(数据 FIFO 空、内部仲裁未获胜、状态机停在某个状态)。配合 SVA 断言效果更好:valid |=> $stable(payload) until ready、AW 握手后最终必见对应 B、计数器永不为负且复位必归零,这几条断言几乎覆盖了生命周期类 bug 的全部表现。
验证时应构造定向用例:随机 stall BREADY/RREADY 数百拍、AW 与 W 反向乱序到达、全部表项占满时再发请求、错误响应早退,确认计数器最终归零、表项全部回收。回归末尾保留一个「排空阶段」——停止激励后只让时钟继续跑,断言所有通道最终归于 VALID=0、计数器归零,可一次性暴露表项泄漏与响应无人接收两类问题。
这一篇在知识体系里的位置
协议全貌见 AXI4 完整协议,单拍规则见 valid/ready 握手,单笔无 burst 从机见 AXI4-Lite 从机设计,多笔在途的 ID 与乱序语义见 AXI4 outstanding 与 ID 保序;多从机互连结构见 AXI Crossbar 与 Interconnect,跨频率加拍见 AXI 寄存器切片,仲裁公平性见 Round-Robin 仲裁器。
总结
AXI4 一笔事务的生命周期就是沿固定通道依赖逐拍握手推进:写事务 AW→W→B、读事务 AR→R,表项在地址握手时分配、在 B 握手或 RLAST 握手时释放;死锁都源于「最后一条通道(尤其 B/R)无人接收导致资源不释放」,防护的核心是响应永远可排空、AW/W 独立可缓存、授信留余量且每条退出路径都归还表项。