axi死锁分析
参考文章:
- (44 封私信 / 80 条消息) 【学习笔记】AXI总线死锁场景分析与学习 - 知乎
- 分享一种总线死锁场景_axi死锁-CSDN博客
- (44 封私信 / 80 条消息) 【犄角旮旯的bug】只不过想把通道对齐,怎么就逻辑环了呢? - 知乎
死锁产生的条件
广义上的死锁包括很多种场景,线程死锁、任务死锁和总线死锁等,而死锁的发生必须具备一定的条件。死锁的发生必须具备以下四个必要条件,即柯林斯四条件
- 互斥条件(Mutual Exclusion):指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放
- 请求和保持条件(Hold and Wait):指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放
- 不可剥夺条件(No Preemption):指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放
- 环路等待条件(Circular Wait):指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源
对柯林斯四条件进行详细案例的解析,比如下面这张图,多播死锁:

- M0需要申请S0和S1的资源,此时已经占用了S0,等待S1资源进行处理
- M1同样需要申请S0和S1的资源,此时已经占用了S1,等待S0资源进行处理
- S0需要在响应M0后才能响应M1,M0需要S1的响应后才能结束对S0的占用,S1在响应M1后才能响应M0,M1需要收到S0的响应后才能释放S1,形成多播死锁
从上面这个例子中可以清晰看出柯林斯四条件
- 互斥条件:S0在响应M0后在结束占用前,无法响应M1的需求;同样,S1在响应M1后结束占用前,也无法响应M0的需求
- 请求和保持条件:S0已经响应了M0,此时又接收到了M1的访问请求;S1同理;
- 不可剥夺条件:S0已经开始响应M0,除非M0结束占用,否则S0无法响应M1;S1同理
- 环路等待条件:M0等待S1响应,S1等待M1释放,M1等待S0响应,S0等待M0释放,形成了一个环路
上面这四个条件同时满足的时候,死锁就发生了
死锁常见原因
对于AXI死锁的常见原因,做的总结如下所示
- 主设备不支持乱序
- 主设备支持outstanding操作
- 从设备具有乱序执行能力
- 系统拓扑存在主/从设备互异的长短路径
- 写操作地址超前写数据发出
- 主设备或互联通路资源不足
下面的案例分析多有体现上面这些原因
死锁场景分析
资源不足导致的死锁
下图展示的是一个两个AXI Master和两个AXI Slave互联的结构。具体的场景为AXI Master1依次发起A1和A2两个写操作,其中A1访问AXI Slave2,A2访问的是AXI Slave1;AXI Master2依次发起B1和B2两个写操作,其中B1访问的是AXI Slave1,B2访问的是AXI Slave2。AXI Slave1接收到的写顺序是A2,B1;AXI Slave2接收到的写顺序是B2,A1。
假设Write Buffer的深度都是8,也就是说两个buffer都可以存储8个beat写数据。假设A1和B1的burst length是9,也就是说发出的数据是10个beat的write data。下面分别对AXI Slave1和AXI Slave2进行分析
- A2先于B1到达,根据AXI协议,write address和write data顺序要一致,因此A2的write data必须先于B1发送给Slave1
- A2的write data依赖于A1的write data全部被总线接收,但是Write buffer1深度只有8,只能暂时接收8个beat的A1 write data
- A1在Write Buffer1中的Write data要发送到AXI Slave2,必须要等到B2的write data全部发送完成
- B2的write data全部依赖于B1的write data全部被总线接收完毕,但是Write buffer1的深度只有8,只能暂时接收8个beat B1的write data
- B1的write data必须等A2的write data发送完之后,才能发送到AXI slave1口
因此,一个死锁场景发生了,依赖条件 B1 -> B2 -> A1 -> A2 -> B1,这个死锁条件的本质是:
- AXI协议规定AXI write addr和AXI write data的顺序必须一致
- Write buffer的深度不够
attention
AXI4才会出现上面的问题,AXI3可能不会出现这个问题,因为AXI3支持写交织,AXI4已经没有写交织了

解决方法
可以通过下面这几种方法来破除这个死锁:
- 把AXI Slave1和Slave2的outstanding能力设置为1,这样会对总线的performance影响很大;如果限制为1,那么A2就不会出现在AXI Slave1上,B2就不会出现在AXI Slave2上
- 把Write buffer的深度加大,使它们至少能各自容纳A1和B1的全部write data beat,这样会使总线的面积增大很多;Write buffer1把A1的write data全部接收之后,就可以转发A2的write data给AXI slave1口,因此A2就可以正常完成了,进而B1、B2和A1也可以完成,打断了死锁链条
- 在总线内部做冒险的检查,如果发现有任何未完成的写操作到不同的接口,那么就先把当前的write操作挡住不往下发,这个方法的应用比较多。总线在收到A2时发现A1已经发给AXI slave2口,但A2是打算发给AXI slave1口,因此总线就先把A2挡在内部,暂时不发给AXI slave1口。等A1结束后,再把A2发给AXI slave1口;对B1和B2的处理也类似,从而打断了死锁链条
乱序死锁
案例一
对于乱序导致死锁的例子如下,首先M0和M1是保持传输源,S0和S1支持乱序传输

- M0以保序同一ID的方式先后发出到S0读请求1和到S1读请求2
- M1以保序同一ID的方式先后发出到S1读请求3和到S0读请求4
- S0支持乱序,先后接到请求1和4,请求4的数据先准备好,4优先在接口上返回给xbar
- S1支持乱序,先后接到请求2和3,请求2的数据先准备好,2优先在接口上返回给xbar
- 由于M0是保序的,必须先接收S0返回的数据1,才能接收S1返回的数据2
- 由于M1是保序的,必须先接收S1返回的数据3,才能接收S0返回的数据0
- 因此,从M0的角度来看,M0的数据1 -> 数据4 -> 数据3 -> 数据2 -> 数据1形成死锁;同理M1也形成了死锁
通过对上面这个案例分析,主要解决方法可以如下:
- 首先Master可以支持乱序,读回来的数据不一定按照发送请求时候的数据
- 在xbar中新增缓存模块,xbar内部进行重排列,保证Master可以按照相同的顺序接收到数据
案例二
下面是一个slave既支持乱序又有多个接口的案例

- 保序的M0先后发出写请求1和写请求2,写请求2的优先级更高;
- S0处于阻塞状态,不能马上接收写数据,因此请求1和请求2都被缓存在入口寄存器片内
- S0解除阻塞后,从寄存器片中接收写请求,此时因为请求2的优先级更高,S0先把请求2从接口的寄存器片取出
- 此时S0需要优先接收请求2数据,但是因为M0是保序发送,只能先发送数据1再发送数据2,形成死锁
长短路径死锁
写通道没有乱序了的话能避免死锁吗?显然不能,上面就已经有一个写死锁的例子了,下面是一个长短路径写死锁的例子

- M0保序先后发出写请求1访问S1,写请求2访问S0;
- M1保序先后发出写请求3访问S0,写请求4访问S1
- M0-S0为短路径,S0先收到了写请求2,之后收到了写请求3,因此S0需要先收到写数据2
- M1-S1为短路径,S1先收到了写请求4,之后收到了写请求1,因此S1需要先收到写数据4;
- S0需要先收取写数据2,写数据2依赖写数据1发出,写数据1依赖写数据4被S1收取完,写数据4依赖写数据3发出,写数据3依赖写数据2被S0收取完,于是形成了长短路径死锁,2 -> 3 -> 4 -> 1 -> 2
info
常见的死锁场景大概就是这几种,从以上几个案例来看,“多主多从、读会乱序、写爱超车、资源不足、路径参差”这几个事总是更死锁问题纠纠缠缠的,因此防御机制也基本是针对这几个方面进行的
死锁的常见防御机制
对齐传输
对齐传输有两个级别,一种是松散对齐就是w不会超越aw但aw可能超越w

有很多的slave其本身就不支持先收取wdata后接收waddr(当然更多的是支持的)。而另外一种是严格对齐,即w不会超越aw同时aw也不会超越w,在总线上会呈现一个一个aw/w的传输时间段。

error
能够看出,如果采用严格对齐的传输方式,那么资源不足死锁中的案例1、乱序死锁案例中的案例2和长短路径死锁的案例就不会发生了。但是在实现通道对齐传输时要避免组合逻辑环的发生
- master端对通道进行对齐,那么w通道必然需要参考aw通道的,为了没有效率损失,那么awvalid会参与到wvalid的逻辑中来(w第一拍和aw实现同拍发送)
- crossbar中进行多路调度,多路调度一种很常见的设计是,有valid了才给ready,就是有请求了才去申请总线,申请到了才能起ready,因此wvalid会作用到wready的逻辑中来
- aw通道需要看上一个aw对应的wlast是不是发完了,发完了才开始发下一个aw请求,因此wready和wlast会作用到awvalid逻辑中

attention
aw通道和w通道的互看行为,导致此处有组合逻辑环出现
资源充足
在我们用一些工具、vip生成interconnect的时候,或者用NIC等组件的时候,大多是可以去分支缓存深度以及分路缓存的。例如根据下面这张图中通过增加Write Buffer的深度可以防止总线死锁。

SSPID
info
SSPID 在这里一般指 Single Slave Per ID,是 AXI / NIC 互连里的 CDAS(Cyclic Dependency Avoidance Scheme,环形依赖规避)之一。它的目标不是“加一把软件锁”,而是在总线互连的入口处限制事务分流,避免 AXI 多 outstanding、多目标、多通道背压组合成死锁。
SSPID 的规则可以一句话概括:同一个入口、同一个方向、同一个 AXI ID,在还有未完成事务时,只允许打到同一个目标 slave,具体可以参考下面这张表:
| 新事务情况 | SSPID 行为 |
|---|---|
| 该 ID 当前没有 outstanding | 放行,并记录这个 ID 当前指向的目标 |
| 该 ID 已有 outstanding,且目标相同 | 放行 |
| 该 ID 已有 outstanding,但目标不同 | stall,直到该 ID 的 outstanding 清空 |
以下面这张图为例,M0先发送写数据请求1到S0,这时候已经被记录下来了,因此M0到S1的就会被先阻塞下来,同样的写数据请求4也会被阻塞下来,这样当M0释放S0的时候,写数据请求3被响应,同样当M1释放S1的时候,写数据2被响应,这样就不会导致死锁。

SAS
SAS(single active slave,单个活跃从设备),与单ID从端模式(SSPID)原理相同,但针对写事务增加了一项检查机制。SAS确保主设备必须等待前一个写事务的所有数据均发送完毕后,才能发起新的写地址请求。在 SSPID 的基础上,专门再管住 AXI 写地址通道 AW 和 写数据通道 W 之间可能造成的死锁。
举个例子详细解释一下SAS新增的检查内容,假设 master 连到互连,后面能访问 DDR 和 OCRAM
1 | AW0: ID=1 -> DDR, burst 8 beats |
这时又来了:
1 | AW1: ID=2 -> OCRAM |
在 SSPID 下,ID=2 和 ID=1 不同,可能允许通过。但在 SAS 下,因为 AW1 要切到 OCRAM,而 DDR 的 W0 数据还没发完,所以 AW1 会被阻塞,等到 W0 的最后一拍 WLAST 已经送出,之后,AW1 -> OCRAM 才能继续。
attention
在OCRAM被阻塞的时候,可能引起乱序锁死案例2的情况
SS
SS(single slave,单从设备),确保交换机的从接口满足以下条件:
- 所有未完成的读事务必须发往同一终端目的地
- 所有未完成的写事务必须发往同一终端目的地
若主设备试图向不同于当前事务类型(读或写)目标地址的目的地发起新事务,网络将阻塞该类型的所有事务,直至该类型下所有未完成事务全部处理完毕。
简单点说就是master在一段时间内只能访问同一个slave,如果要访问其他的,那么需要等对前一个slave的访问全部完成,这种严格的情况下已经可以规避绝大多数死锁问题了,如下所示
1 | ARID=1 -> DDR 已放行,还没返回完 |
info
为什么 SS 能防锁死 ?
AXI 互连最容易出问题的地方,是 master 可以同时发多个outstanding transaction,并且这些事务可能去不同 slave。不同 slave 的返回顺序、响应通道、缓冲资源、仲裁器和 READY/VALID 背压组合起来,可能形成环形依赖。
对SSPID,SAS,SS总结成一张表,如下所示
| 机制 | 限制粒度 | 核心规则 | 主要管什么 | 防锁死强度 | 性能影响 |
|---|---|---|---|---|---|
| SS | 整个入口/ 同一方向 | 只要该方向还有 outstanding,就只能继续访问同一个 slave | 读方向 AR/R、写方向 AW/W/B | 最强 | 最大 |
| SSPID | 同一个 AXI ID | 同一个 ID 在 outstanding 未清空前,只能访问同一个 slave | 同 ID 的读/写事务分流 | 中等 | 较小 |
| SAS | SSPID + 写数据状态 | 在 SSPID 基础上,若前一写事务的 W 数据还没发完,不允许 AW 切到另一个 slave | 主要防 AW/W 分离导致的写通道环形依赖 | 强于 SSPID,弱于 SS | 中等 |
距离说明三种情况下的阻塞和放行:
| 场景 | SS | SSPID | SAS |
|---|---|---|---|
| ID=1 -> DDR 未完成,又来 ID=2 -> OCRAM 读 | 阻塞 | 放行 | 放行 |
| ID=1 -> DDR 未完成,又来 ID=1 -> OCRAM 读 | 阻塞 | 阻塞 | 阻塞 |
| ID=1 -> DDR 未完成,又来 ID=2 -> DDR 读 | 放行 | 放行 | 放行 |
| AW -> DDR 已接收,DDR 的 W 数据未发完,又来 AW -> OCRAM | 阻塞 | 不一定阻塞,取决于 ID | 阻塞 |
| AW -> DDR 已接收,DDR 的 W 数据未发完,又来 AW -> DDR | 放行 | 放行,若 ID 规则满足 | 放行,若 SSPID 规则满足 |
