OpenSTA 最大扇出违规:六负载复现、约束定位与 buffer 拆分
OpenSTA 最大扇出违规表示一个驱动输出连接的负载数超过了允许值。它属于设计规则检查,不等同于建立时间或保持时间失败:即使路径 slack 为正,扇出仍可能超限。本篇用一个 BUF 驱动六个 BUF 的最小网表,说明怎样读违规数值、追溯门限,以及为什么放宽约束不能代替修复。若报告同时缺少时钟路径,应先转到 OpenSTA no clocks found 排查。
本文要回答的问题
fanout=6、limit=4和slack=-2分别说明什么?set_max_fanout与 Liberty 里的max_fanout谁提供门限?- OpenSTA 2.0.17 能否直接使用
report_check_types -max_fanout? - 提高门限后不再报违规,为什么不能算电路已经修好?
- buffer 拆分后应怎样重新验证?
读这张图只抓住“一个驱动、一条网络、六个输入引脚”。扇出按负载引脚和输出端口计数,不是数导线,也不是数下一级单元的输出。两个负载组只是排版分组,实际都直接连在 n 上,所以合计为 6,而不是两组各自通过一次检查。
在层次化设计里还要先展开到正确的网络对象:跨模块的一条逻辑连接可能呈现为多段层级网线,不能只数当前模块看得见的引脚,也不能把同一负载在多级层次中的名称重复累计。本例采用单层门级网表,避开了这个干扰,因而六个输入引脚可以直接与报告逐项对上。
一、先确认 OpenSTA 2.0.17 的命令边界
一些新版示例使用 report_check_types -max_fanout -violators,但这条选项组合不能原样套到本文版本。真实运行返回 not a known keyword or flag,而且脚本正常走完后退出码仍为 0。若自动化只看退出码,就会把“命令没有执行”误判成“没有违规”。
CASE: probe report_check_types -max_fanout on OpenSTA 2.0.17
Error: compat.tcl, 3 report_check_types -max_fanout is not a known keyword or flag.
CASE_EXIT compat=0因此本文不把旧版不支持的选项写成可用命令,而是保留 set_max_fanout 约束,再通过 OpenSTA 的 get_pins、get_ports 网表对象查询计算负载数。下面出现的 MAX_FANOUT_VIOLATION 是实验脚本的判定标记,不是 OpenSTA 内建报告行。换用其他版本时,应先验证命令选项,再沿用对应的原生报告。
| 检查动作 | 本文版本的结果 | 能证明什么 |
|---|---|---|
link_design top | 返回 LINK_OK | 单元引用和网络连接已解析 |
report_check_types -max_fanout | 报未知选项 | 该版本不能按此方式取报告 |
查询 n 的输入引脚与输出端口 | 计得 6 个负载 | 给出可复算的扇出基数 |
| 比较负载数与门限 | 得到 slack=-2.0 | 当前门限下存在违规 |
二、用六个负载复现 OpenSTA 最大扇出违规
实验 Liberty 只定义 BUF_X1。网表中的 u_drive 驱动网络 n,u_load0 至 u_load5 的输入都接到这条网络;六个输出分别连接到顶层 y[0] 至 y[5],不会计入 n 的负载。
wire n;
BUF_X1 u_drive (.A(a), .Y(n));
BUF_X1 u_load0 (.A(n), .Y(y[0]));
BUF_X1 u_load1 (.A(n), .Y(y[1]));
BUF_X1 u_load2 (.A(n), .Y(y[2]));
BUF_X1 u_load3 (.A(n), .Y(y[3]));
BUF_X1 u_load4 (.A(n), .Y(y[4]));
BUF_X1 u_load5 (.A(n), .Y(y[5]));设计级门限设为 4,与实验库中输出引脚的 max_fanout : 4.0 保持一致。检查脚本把网络输入引脚数和输出端口数相加,再计算 limit - fanout。关键输出如下。
driver=u_drive/Y 指向驱动输出,net=n 指向检查对象;fanout=6 是从链接后的网表对象统计而来。门限为 4,所以余量为 4-6=-2,需要减少两个负载才能刚好回到该门限。这里不需要时钟,也不应拿时序路径的 MET 或 VIOLATED 来解释这项设计规则结果。
三、门限来自哪里,决定该改哪一层
看到违规后,不要马上把数值改大。先确认约束来源:库属性描述单元能力或工艺限制,设计约束描述当前项目允许的拓扑预算,具体实例或引脚约束还可能覆盖局部对象。不同项目的优先级要以所用流程为准,不能凭一个最小库推断所有 PDK 的行为。
| 门限来源 | 典型写法 | 排查重点 | 是否适合直接放宽 |
|---|---|---|---|
| Liberty 单元或引脚 | max_fanout : 4.0 | 是否随库版本、 corner 或单元型号变化 | 通常不适合,先确认库含义 |
| 当前设计 | set_max_fanout 4.0 [current_design] | 是否来自项目统一约束 | 只能按设计规范变更 |
| 局部对象 | 对端口、引脚或设计对象设置 | 是否意外覆盖全局值 | 先查覆盖原因 |
| 临时诊断脚本 | 仅在对照组设为 6 | 是否只为验证门限触发关系 | 不得作为修复提交 |
扇出违规也常与转换时间、负载电容相互影响,但三者不是同一张报告。扇出数降为 4 只说明数量约束满足,并不自动保证 slew、延时和布线后的电气表现都合格。修复后仍要回到完整约束集检查。
四、提高门限只做诊断对照
第二组完全不修改 top.v,只把脚本中的诊断门限从 4 改为 6。输出从 slack=-2.0 变为 slack=0.0,恰好证明两组差异来自门限,而不是网表负载发生变化。
CASE: diagnostic max_fanout limit 6, actual loads 6
MAX_FANOUT_CHECK net=n driver=u_drive/Y limit=6.0 fanout=6 slack=0.0
MAX_FANOUT_OK net=n loads=6 limit=6.0
RELAXED_CHECK_DONE: no design changes
CASE_EXIT relaxed=0这一组的价值是定位:如果同样的 6 个负载只在门限 4 时失败,检查逻辑和约束触发关系就清楚了。它没有降低电容、改变驱动能力,也没有改变任何连接关系,所以正文和脚本都明确标注 no design changes。若项目规范仍是 4,这组 OK 不能作为验收结果。
五、实际修复是拆分负载并复跑
常见结构修复是在综合或物理实现阶段插入缓冲级,把原来一个输出直接承担的六个负载拆成两个分支。下面只说明目标拓扑:根驱动连接两个分支缓冲,每个分支各带三个负载。是否保留原驱动、选何种单元以及放在哪个层次,应由实际实现流程决定。
不要把这张图理解成 OpenSTA 会自动完成 buffer 插入。本文 OpenSTA 只负责读取设计和提供查询结果,结构变化必须在外部实现流程中完成。新网表可能改变根驱动的直接扇出、各分支的延时与转换时间,因此验收至少包括:目标网络不再超限、没有新增扇出违规、原有时序检查仍通过。
代码获取:最小 Liberty、六负载网表、兼容性探针、查询脚本及原始输出位于 geek-doc.cn/experiments/ts-opensta-max-fanout-violation,安装 OpenSTA 后执行 bash run.sh 可复现三组结果。
实验环境
- 机器/系统:本地 Docker 容器,Ubuntu 24.04;宿主为 macOS。
- 工具版本:OpenSTA 2.0.17,Ubuntu 官方独立二进制包;未使用 OpenLane。
- 日期:2026-10-31。
- 完整命令:在实验目录执行
bash run.sh,依次运行compat.tcl、violation.tcl、relaxed.tcl,合并标准输出与标准错误并记录退出码。 - 实验边界:教学 Liberty 不代表真实工艺;门限 6 仅作诊断,对照脚本没有插入 buffer,也没有修改网表。
这一篇在知识体系里的位置
本篇处理设计规则层面的扇出数量,与时序路径是否建立是两个维度。OpenSTA 最大扇出违规排查前若发现无路径,可先确认时钟附着;如果扇出正常但端点仍漏约束,则继续看 unconstrained endpoint 排查。实际芯片流程中,还应把修复后的网表交回转换时间、功耗和时序检查。
总结
OpenSTA 最大扇出违规的核心是“实际负载数减门限”:本例一个驱动连接六个输入负载,门限为 4,因此余量为 -2。OpenSTA 2.0.17 不支持 report_check_types -max_fanout,实验改用网表对象查询复算,并保留命令兼容边界。把门限放宽到 6只能用于定位,真正修复要在实现流程中拆分负载、导出新网表,再复查扇出、时序和电气约束。