1. 前言
总线设计中经常会发生死锁,关键死锁场景还不一定好发现,而且也不好依赖激励去打出来,很多时候要靠正向分析去发现。本文分享一个AXI总线中比较容易发生死锁的场景。
2. 死锁场景
如图1所示,有一个2-to-2的AXI总线,上面接两个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。
图1 2-to-2 AXI总线
假设Write buffer1深度为8,Write buffer2深度也为8,也就是它们各自可以存储8个beat的write data。A1和B1的burst length为9,也就是A1和B1分别会发送10 beat的write data。
我们从AXI slave和AXI master口来看:
A2先于B1到达,根据AXI协议(write address和write data顺序要一致),因此A2的write data必须先于B1发送给AXI 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全部发送完到AXI slave2上。
B2的write data会依赖于B1的write data被总线全部接收,但是因为Write buffer1深度只有8,只能暂时接收8个beat B1的write data。
B1的write data必须等A2的write data发送完之后,才能发送到AXI slave1口。
因此,一个死锁场景就发生了,如图2所示,①依赖于②,②依赖于③,③依赖于④,④依赖于①,一个环形资源依赖(①→②→③→④→①)产生了。这个死锁产生的本质是:
AXI协议规定AXI write和AXI data的顺序必须要一致;
Write buffer深度不够;
图2 2-to-2 AXI总线死锁示意图
3. 解决办法
这个死锁比较常见,也比较好解决,可以通过以下几种方式:
把SLV1或SLV2的outstanding能力限制为1,这样会对总线的performance影响很大;如果限制SLV1为1,那么A2就不会出现在AXI slave1口上了,B1就可以正常完成,进而B2和A1也可以完成,打断了死锁链条。
把Write buffer的深度加大,使它们至少能各自容纳A1和B1的全部write data beat,这样会使总线的面积增大很多;Write buffer1把A1的write data全部接收之后,就可以转发A2的write data给AXI slave1口,因此A2就可以正常完成了,进而B1、B2和A1也可以完成,打断了死锁链条
在总线内部做hazard的检查,如果发现有任何未完成的写操作到不同的接口,那么就先把当前的write操作挡住不往下发,这个方法的应用比较多。总线在收到A2时发现A1已经发给AXI slave2口,但A2是打算发给AXI slave1口,因此总线就先把A2挡在内部,暂时不发给AXI slave1口。等A1结束后,再把A2发给AXI slave1口;对B1和B2的处理也类似,从而打断了死锁链条。
4012