BUS设计中的DeadLock死锁问题

news/2025/2/23 18:46:17/文章来源:https://www.cnblogs.com/xianyuIC/p/18147707

本文讨论一下 BUS 设计中的 DeadLock 死锁问题,或者叫做 Cyclic Dependency 循环依赖问题。其含义是指 A 的动作导致 B 的动作无法进行下去,同样 B 的动作导致 A 的动作无法进行下去,二者相互依赖,形成死锁。

1 AXI握手死锁

在学习 AXI 协议时,手册中提到了 AXI 握手死锁,即 VALID 信号和 READY 信号的握手死锁问题。AXI4 协议中说道:

这里的表述似乎说明,VALID 信号和 READY 信号相互之间不要依赖,即不要根据对方的状态来决定自己是否拉高或拉低。但是在后面的描述中,我们发现了更细致的描述:

不仅是 Write address channel,其他通道的 VALID 信号和 READY 信号也都有类似的规定,再结合前文,我们可以总结成一句话:VALID 和 READY 之间没有依赖关系,谁先谁后都可以,但是源端拉高 VALID 后必须保持住,直到终端拉高 READY。

2 AXI行为死锁

AXI 事务传输中,有两类经常出现的死锁:响应通道死锁和写通道死锁。这两类死锁在 NIC400 中提供了相应的解决方案。

2.1 响应通道死锁

先复习 AXI 顺序模型的两个概念:

  • Master 发出相同 ID 的事务,那么接收响应时必须按照顺序来接收。
  • Slave 收到不同 ID 的访问,可以乱序回复响应。(例如DDR经常这样)

(1)问题描述

于是乎,在下图的蝴蝶结的结构中,由于 SlaveA 进行了乱序响应,就形成了死锁。

注意,除了读事务在这种结构中会出现死锁问题,写事务响应也同样会出现,因为写事务响应也是可以乱序或者加 RegSlice 的。

(2)解决方案

解决这类问题有两个思路:

  1. 不要出现蝴蝶形路由结构。
  2. 不要出现相同ID。

所谓蝴蝶形结构,高度概括一下是可以形成一个逻辑环,事务之间可以饶一个圈圈。这种蝴蝶形结构很容易出现,很多时候没有办法,除了上面画出的单个 Matrix 中会出现蝴蝶形结构,多个 Matrix 之间同样可能出现这种结构,如下图所示:

红色圈起来的部分,简化一下其实也就是上文说的蝴蝶形结构,它同样是形成了一个事务环。想要避免蝴蝶形结构比较难,除了上述看得到的 Slave,在互联中还有看不到的 Default Slave,同样可能构成蝴蝶形结构,真的防不胜防。

既然蝴蝶形结构难以避免,那么可以从“不要出现相同ID”的思路去解决,NIC400 中就此思路提供了两种解决方案:SSPID(Single Slave Per ID)或者 SS(Single Slave)。

  • Single Slave Per ID,简称 SSPID,意思是:Master 发出的事务,同一个 ID 只能对应一个 Slave。当出现 Master 发出两个相同 ID 给不同的 SlaveA 和 SlaveB 时,NIC400 将 SlaveB 停滞住,等 SlaveA 的响应回来后,再发送 Slave B 事务,这样就不会出现死锁问题了。
  • Single Slave,简称 SS。意思是:不管是啥 ID,总之先完成第一笔事务前,停滞后面的事务,等第一笔事务完成后再发出下一笔事务。简单粗暴,面积小,时序收敛影响小,但是牺牲了 AXI 并发性能,适用于对并发性能无要求的配置通路上。

2.2 写通道死锁

再复习一下 AXI 顺序模型的一个概念:

  • AXI3 有 WID,支持交织,但是 WDATA 的第一笔必须和 AW 顺序一致。
  • AXI4 没有 WID,不支持交织,WDATA 必须和 AW 顺序一致。

(1)问题描述

不管是 AXI3 还是 AXI4,WDATA 的第一笔必须和 AW 顺序一致,这在某些情况下就会出现死锁问题。

在某次传输中,Master 发出 cmd_A 和 cmd_B,然后发出 data_A 和 data_B,但是 cmd_A 可能由于某种原因出现延迟(例如 SMMU redirect 场景),反而更晚到 Slave,如下所示:

而 Slave 那边要遵循 AXI 协议规定,先收到 cmd_B 自然要先接收 data_B,可是它先得到的又是 data_A,这就出问题了。

这是同一个 Slave 的场景,但是多个 Slave 构成的场景中也可能出现这个问题,例如上面的那个多 Matrix 情况,M0 本来先发送命令到 S1,再发送命令到 S0,但是 M0 到 S1 的路径上存在 RegSlice,导致命令晚于 M1 给 S1 的命令,结果出现死锁。

(2)解决方案

解决这类问题有个思路是:写传输路径添加 ReOrder Buffer,数据到终端前,数据按照 Slave 实际接收的命令顺序传下去。但是弊端是太浪费面积了,ReOrder Buffer 面积很大,得不偿失。

NIC400 中提供了 SAS 和 SS 两种解决方案:

  • Single Active Slave,简称 SAS,意思是:上一笔写事务的写数据下发完后,才能发送下一笔写事务的写命令。从而针对性的避免了写通道的死锁问题。
  • Single Slave,简称 SS。意思是:不管是啥 ID,总之先完成第一笔事务前,停滞后面的事务,等第一笔事务完成后再发出下一笔事务。简单粗暴,面积小,时序收敛影响小,但是牺牲了 AXI 并发性能,适用于对并发性能无要求的配置通路上。

Single Slave(SS)既可以解决响应通道的死锁问题,也能够解决写通道的死锁问题,就是太暴力了,对性能影响大。

3 拓扑结构死锁

由于 AXI 的信号要求,出现了 AXI 行为死锁,而在 NOC 设计中,会将 AXI 转换为内部的 Packet 包,从而避免单 Matrix 中出现的 AXI 行为死锁。但是 NOC 不是万能的,仍然面临拓扑结构型的死锁。下面列举几种常见的拓扑结构死锁。

3.1 迂回形死锁

迂回型死锁,即 Roundabout 型死锁,指的是进入 Switch 的 Packet 以循环方式互相阻塞住,原本要出去的包和新的进来的包之间具有依赖性,如下所示:

A 发送数据到 NoC 的 M0 端,然后经过 Switch 路由到 S0 端,再给到 B。但是 B 之后经过一系列别的路由,饶了一圈到 NoC 的 M1 端,然后经过 Switch,到达 S1 端,最后到达目的地 C。这种结构会出现死锁,原因是 M0 到 Switch 的 VALID 信号会占住 Switch,使得后面 M1 要使用 Switch 时无法抢占。S0 出去和 M1 进来之间形成依赖,造成死锁。

3.2 拆分重组死锁

拆分重组死锁,即事务被拆分后,Response 的拆分段会锁住一个等待其他 Response 段的响应路径的 Switch,如下所示:

A 的路径为红色,经过 NoC 0 和 NoC 1 到达 B,但是中途经过 NoC 时,原本 1 笔读命令被拆分为 2 笔读命令(NoC 特性,是否拆分读响应看情况,有它的一套规则),于是 B 要回复两笔读响应。此时 C 也发送数据,路径为蓝色,经过 NoC 0 和 NoC 1 后到达 D,然后 D 回复响应。可是在某种情况下,这会形成死锁,在 Switch0 处,B 响应的第 1 笔占到了仲裁,D 响应于是在这里等待。但是在 Switch1 处,D 响应占到了仲裁,B 响应的第 2 笔无法通过。于是乎,A 在等待第二笔读响应,也就是 Switch0 在等待第 2 笔读响应前不会释放仲裁,但是 D 响应由于没有达到 C,就一直占着 Switch1,于是形成了死锁。

3.3 回环死锁

NoC 可以设计为 Ring 结构,每一个 Switch 上都挂有 Master 和 Slave,如下所示:

如果每个 Master 发出的包都很长,则会出现死锁,因为每个包都在走,但是都走不完,被阻塞住了。

Ring 结构是一种比较极端的拓扑结构,其实不需要 Ring 结构,我们衍生一下,其他拓扑结构也可能出现类似问题,例如其中一条通路的事务无法完成(可能Slave Buffer设计不合理,满了),从而影响到了另一条通路的事务无法完成,这在实际项目中也是常见的

3.4 解决方案

这类拓扑结构型死锁,目前没有很好的解决方案,有一种方案是将所有项目中的所有总线做在同一个 BUS 工程上,然后用 BUS 工具内部的 DeadLock 检测算法去检查死锁,其内部的检测算法究竟是怎么做的不得而知。

可是如果真的这样做,至少会带来两个问题:一是当出现某处改动时,往往牵一发而动全身,重新生成总线后,需要各个 Sys Owner 重新集成各自的总线模块,消耗大量人力。二是如此巨大的 BUS 工程,那么多的 Master 和 Slave,容易造成 BUS 工具的 GUI 界面卡死,破坏心情。

4 读写依赖死锁

很多 NoC IP 在设计时,都会采用读写共用通道,这在不会同时出现大量并发的读操作和写操作时,对节省走线是有巨大帮助的。然而如果 Master 或 Slave 的读写存在某种依赖,则会导致共用读写通道的 NoC 总线出现死锁现象。

4.1 NoC+Master型

某些 Master IP 在设计时,读写 Buffer 共享(如某些 DMA),只有 Buffer 里读到有一定数据时,才能够允许发送写数据。某次传输时,Master IP 发送了过多的读命令,而其 Buffer 深度不足以存储所有的读数据,但是它可以继续发写命令,这可以让 Buffer 释放一些空间,但是 NoC 采用了读写共用通道,也就是说此时 NoC 里都是读命令,无法接收写命令,于是卡死在这了,形成了 DeadLock。

4.2 NoC+Slave型

某些 Slave IP 在设计时,读写 Buffer 共享(如 DDR),只有Slave Buffer 里的读数据往上游排出,才能接收写数据。NoC + Slave 型死锁出现了,例如这时恰好 NoC 里都是写命令和写数据,但是下游 Slave 接收不了,必须先读走,NoC 又正在传输写数据,无法读走 Slave 提供的读数据,于是卡死在这了,形成了 DeadLock。实际情况中如果上游是连续读,可能中间不会出现写命令和写数据,但是读得差不多了,Master 开始发送写命令和写数据,此时 NoC 需要等读数据完全回给上游后,才能腾出通道处理写命令和写数据,这就会造成带宽下降。

4.3 验证方法

读写依赖死锁可以在验证时发现,具体可以这样做:

  • 连续发送足够多的写命令,直到下游无法继续接收写命令(AW_READY=0),立即将 B_READY=0,然后发送读命令,观察是否能完成。
  • 连续发送足够多的读命令,直到下游无法继续接收读命令(AR_READY=0),立即将 R_READY=0,然后发送写命令,观察是否能完成。

4.4 解决方案

Master、NoC、Slave,三者单独的读写共享都没有违反 AXI 协议规定,都没有什么问题,但是三者中的任意两个连在一起,就会形成读写依赖死锁。解决方案有以下几种:

  • 修改 Master 和 Slave 的读写依赖性。某些情况下可能难以做到,例如有些设计本身就是要先读回数据,运算后再写出去。那么可以这么做:
    • 增大共享 Buffer,缺点是有时候开销过大。
    • 读数据未完成前,不要发送写命令。
  • 修改总线设计,不采用读写共用通道,解决死锁或带宽下降问题。
    • 修改 NoC 设计,不管是 FlexNoC 还是 NI-700,都可以将读写共用通道修改为读写分开通道,当然这会增加走线和开销。
    • 弃用 NoC 设计,改用 NIC400 等读写分开通道的 BUS IP。

 

参考资料:

[1]  AMBA® AXIand ACEProtocol Specification 

[2] SOC常见问题-axi deadlock

[3] NIC400总线死锁成因及解决方法

[4] 移知课程:AXI课程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/888510.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

字符串-str

字符串-str 1.1 特点需要加上引号,单引号与双引号都可以,包含了多行内容的时候还可以用三引号name = rock #报错,没有引号识别为变量名, name = "rock" print(name) name = kyle print(name) name = """rock #多行内容时使用三引号,区分三引…

X00221-基于多智能体强化学习的车联网频谱共享python完整代码

研究基于多智能体强化学习的车联网频谱共享问题,其中多个车与车(V2V)链路复用由车与基础设施(V2I)链路占用的频谱。由于高速移动的车联网环境中信道变化快速,导致基站无法收集到准确的瞬时信道状态信息,从而无法进行集中式资源管理。对此,将资源共享建模为一个多智能体…

Week01【ElementPlus Layout 布局】

Week01【ElementPlus Layout 布局】 📅 2025/02/22 Layout 布局【ElementPlus】 通过基础的 24 分栏,迅速简便地创建布局。组件默认使用 Flex 布局,不需要手动设置 type="flex"。 请注意父容器避免使用 inline 相关样式,会导致组件宽度不能撑满(宽度坍塌)。. …

Linux 中 source命令 和 bash命令的区别

001、 Linuxbash和source命令的区别在于,当你用bash命令执行脚本时,它告诉Linux内核创建一个新的Bash进程来读取和执行脚本,将输出复制到原先的shell进程中,并显示下来. 然而source命令是一个外置的shell,它读取和评估当前shell进程中的文件。为此,脚本所做的所有修改都将…

字符串的编码及解码

1. 字符串的编码及解码解释str类型转换为bytes类型为编码bytes类型转换为str类型为解码2. 字符串的编码str类型转换为bytes类型使用字符串encode()方法语法格式:str.encode(encodeing= utf8,errors=strict/ignore/replace)出错方式:strict:严格的;报错。 ignore:忽略;rep…

3.正向传播与反向传播 - 学习率LR - Batch size - 激活函数 - 损失函数

正向传播尽量降低损失函数梯度梯度是一个向量(矢量),函数在一点处沿着该点的梯度方向变化最快,变化率最大。换而言之,自变量沿着梯度方向变化,能够使应变量(函数值)变化最大。如图:如果想要 w 下降最快就沿着梯度的负方向下降,就能降低损失函数方向传播更新各个参数的…

Qt报错error: member access into incomplete type QTcpSocket

现象解决办法 在mainwindow.cpp文件中添加头文件。 #include <QTcpSocket>

Spring Boot中如何优雅地读取Jar包中的Resources目录下的文件

在Java的Spring Boot项目中,我们经常需要从resources目录下读取配置文件或其他资源文件。在本地开发环境中,我们可以轻松地使用绝对路径访问这些文件,但项目一旦打包成Jar包并部署到服务器上,这种访问方式就失效了。 因此,掌握在Jar包中读取resources目录下文件的方法至关…

开学测试总结owo

经过这次的小测,在这次测试中,我个人认为最大的变化,就是逻辑性增强,sql语句的要求更多了, 这就反应了对于web应用开发这门课对我们专业的用处,还有,前端页面的美化,我认为前端代码,多 数交给AI就可以了,但主要是你自己要明白需求到底是什么,你要完成的项目是什么,…

get current user

02 - 从SpringSecurity中获取当前用户 一行搞定 SecurityContextHolder.getContext().getAuthentication().getPrincipal();这里自定义了一个UserDetailsImpl类实现UserDetails接口, 此时loadUserByUsername方法的返回对象的类型也要改成自定义类型(01篇最后那样), 不然报错。测…

自编译Frp 实现二次开发

在使用过程中,我们都是利用作者打包好的包,直接使用。但是现实中,我们可能需要对其进行二开。 因而,本文简单的为大家介绍下frp的二次开发。以修改frps dashboard为例。现在让我们一起来学习吧。修改之前的界面修改后效果 安装GO环境 因为,Frp是基于GO开发的,所以我们需要…

从黑盒到透明:AI Agent 运行监控实战!

你是否遇到过这样的情况:辛辛苦苦开发的 AI Agent 突然失灵了,却不知道是哪个环节出了问题?今天给家人们分享一下如何让 AI Agent 的运行过程透明化。 一、为什么要监控 AI Agent? 传统的对话系统就像一张预先画好的地图,用户只能按照既定路线前进。而 AI Agent 则像是一位…