以下内容总结自《深亚微米FPGA结构与CAD设计》- 作为一个读书笔记与大家共享。部分内容为AI补充,不对的地方还请指出。
目录
[一、为什么要引入 Elmore 延时模型?](#一、为什么要引入 Elmore 延时模型?)
[三、Elmore 延时的增量计算](#三、Elmore 延时的增量计算)
[四、直接搜索算法:从宽度优先到 A* 变种](#四、直接搜索算法:从宽度优先到 A* 变种)
[4.1 TotalCost 公式](#4.1 TotalCost 公式)
[4.2 伪代码框架](#4.2 伪代码框架)
[五、动态基本成本:高扇出线网的"星形 vs 树形"博弈](#五、动态基本成本:高扇出线网的“星形 vs 树形”博弈)
[7.1 延时提取](#7.1 延时提取)
[7.2 时序分析](#7.2 时序分析)
承接上篇:在保证布通的基础上,怎么让 FPGA 跑得更快?
一、为什么要引入 Elmore 延时模型?
传统的很多 FPGA 布线算法采用的是线性延时模型,即:
在简单场景下(单段线、单个开关),这个近似还过得去。但在 FPGA 里大量存在:
- 多级串联传输管
- 多扇出线网(树形拓扑)
- 混合使用传输管和缓冲器的复杂结构
这时,线性模型会出现两个严重问题:
-
级联传输管
对于 (M) 个传输管串联,线性模型估计:
而 Elmore 模型给出:
延时随 M2 增长,远比线性模型悲观,也更接近真实。
-
多扇出拓扑选择错误
在某些多分支拓扑(书中图 4.13)里,两种拓扑在线性模型下延时相同,但在 Elmore 模型下差异明显:
如果 Rbout<2Rpass(典型情况),右边拓扑反而更快,但线性模型无法区分。
再加上缓冲器对大电容负载的"隔离作用",线性模型很难做出正确判断。Elmore 模型在这方面的精度要高得多,用它来指导布线时序优化意义更大。
二、时序驱动布线的成本函数
时序驱动布线的目标,不再只是"让所有线都走通",而是希望关键路径越短越好。
为此,书中为每个线网的每个接收端(sink)定义了关键度(criticality):
- (i):线网编号
- (j):该线网的某个接收端
- slack(i,j):从源到该端点的时序裕量
- Dmax:当前电路关键路径延时
- MaxCrit:关键度上限(一般略小于 1,如 0.99)
这样定义后:
- 关键路径上的连接:slack≈0⇒Crit≈MaxCrit
- 非关键连接:slack 越大,Crit 越小
时序驱动布线的节点成本写成:
解释一下:
- 对关键度高的连接,第一项占主导:直接用 Elmore 延时做代价
- 对关键度低的连接,第二项占主导:类似布通率驱动布线,更看重拥挤度和资源使用
三、Elmore 延时的增量计算
想在布线搜索过程中实时用 Elmore 延时,前提是每加一个新节点时,能快速算出延时增量。
书中引入了两个量:
- Rupstream(n):从源端到当前节点 (n) 为止的"上游电阻"
- Ctotal(n):当前节点的总电容(线电容 + 开关寄生)
上游电阻递推公式大致如下(简化写法):
-
如果通过缓冲器开关到达节点 (n):
-
如果通过传输管串接到达:
节点 (n) 的 Elmore 延时分量近似为:
由于 RmetalRmetal 是分布式电阻,这里采用了常见的"等效一半在上游、一半在下游"的近似。
在布线树中新增一个分支(连接新 sink)后,需要沿着这条新分支向上回溯到最近的缓冲器,把这些节点的 Elmore 延时做增量更新。
四、直接搜索算法:从宽度优先到 A* 变种
如果沿用上篇中的宽度优先波前扩展算法,每条连接的每一次扩展都用"纯路径成本"(PathCost)排序,那么在引入 Elmore 延时和关键度之后,会遇到两个问题:
- 优先队列每连一个新 sink 都得清空重来 → 开销太大
- 延时和拥挤度一起参与时,Cost 不再是严格单调的"路径图函数",容易出现"走个小回路反而更优"的情况
书中借鉴了 A* 搜索的思想:为每个节点增加一个"预期成本"(ExpectedCost)并用总成本排序。
4.1 TotalCost 公式
对于当前扩展的线网 (i)、接收端 (j),定义:
- PathCost(n):从当前部分布线树到节点 n 的累积 Cost
- ExpectedCost(n,j):从 n 到 sink (j) 的预估最小成本(假设不再遇到拥挤)
总成本为:
其中 αα 是一个控制搜索"贪心程度"的参数:
- α=0:退化为宽度优先
- α=1:类似标准 A*
- 实测 α≈1.2时性价比最好
4.2 伪代码框架
下面是书中伪代码的简化版,保留关键逻辑:
for 每条线网 i {
动态更新该线网相关的基本成本 b(n); // 后面讲动态成本
rip_up 旧的布线树 RT(i);
RT(i) = {source 节点};
// 按关键度从高到低依次布各个 sink
for 每个 sink j (按 Crit(i,j) 降序) {
// 初始化优先队列:把当前树上的所有节点作为起点
init PriorityQueue with nodes in RT(i);
for n in RT(i):
PathCost(n) = Crit(i,j) * delayElmore(n);
TotalCost(n) = PathCost(n) + α * ExpectedCost(n, j);
while (sink j 尚未到达) {
取出 TotalCost 最小的结点 m;
// 只有当新的 PathCost/TotalCost 均优于历史值才扩展
if (PathCost(m) < StoredPathCost(m) &&
TotalCost(m) < StoredTotalCost(m)) {
更新 StoredPathCost/StoredTotalCost(m);
for m 的所有邻居 n {
根据当前开关类型更新 Rupstream(n);
计算 Cost(n) = 时序项 + 拥挤度项;
PathCost(n) = PathCost(m) + Cost(n);
TotalCost(n) = PathCost(n) + α * ExpectedCost(n,j);
把 (n, TotalCost, PathCost, Rupstream) 放入 PriorityQueue;
}
}
}
// 找到了 sink j,沿路径回溯,把路径加入 RT(i)
更新路径上节点的当前拥挤度 p(n);
把这些节点加入 RT(i),并保存 Rupstream(n);
增量更新 RT(i) 中相关节点的 Elmore 延时;
}
// 所有 sink 连接完,对该线网执行时序更新
}
// 一轮迭代后:
更新所有节点的历史拥挤度 h(n);
做一次全局时序分析,得到新的 slack(i,j)、Crit(i,j);
注意几点:
- PriorityQueue 中每个元素存三元组:(TotalCost,PathCost,Rupstream)
- 每当完成一个 sink 的连接,必须对 PriorityQueue 清空重建
- 对每个节点还维护
StoredPathCost/StoredTotalCost,防止回路导致"假优化"
五、动态基本成本:高扇出线网的"星形 vs 树形"博弈
在高扇出线网(如全局时钟、复位等)上,有一个典型权衡:
- 全靠传输管串起来:树规模很大,负载电容集中在源端,延时会迅速变差
- 尽量多用缓冲器:更像"星形网络",扇出被缓冲器分担,单条路径延时下降,但布线资源更贵
为此,书中引入了一个随扇出数 k 变化的基本成本调整:
对于通过传输管连接其他相同类型线段的节点,随着扇出 (k) 增加,其基本成本逐渐上升;而通过缓冲器相连的线段保持接近平均值。
整体逻辑是:
- 一开始,并不强行用缓冲器,而是允许一定程度的传输管连接;
- 随着扇出增多,"再往这片树里加节点"的代价越来越高,布线器自然倾向于开辟新的带缓冲器分支;
- 同时还要考虑拥挤度惩罚,避免为了"时序好看"把拥挤搞得不可收拾。
实践结果:
- 动态成本平均能带来约 4% 的电路速度提升
- 在忽略拥挤、只追求最小延时时,电路延时比理论最小值高 10%;加入动态成本 + 考虑拥挤后,延时只高 1% 左右,且比"不用动态成本"快 3% 左右
也就是说:动态成本在不伤布通的前提下,帮你接近"理想延时"。
六、复杂度与性能:为什么时序驱动反而更快?
从理论上看,这种直接搜索 + Elmore 延时的算法复杂度大致是:
- 非拥挤情况:对 (k) 个终端的线网,时间复杂度 O(k2logk)
- 严重拥挤情况:最坏 O(k⋅RlogR),(R) 为布线图节点数
但书中强调,极端最坏情况几乎不会在真实电路中出现。在大量 MCNC 大型电路上的实测结果显示:
- 时序驱动布线器在"低难度布线"(W=1.2Wmin左右)下:
- 电路平均速度是布通率驱动布线器的 2.6 倍
- 所用布线轨道数只多约 7%
- CPU 时间方面:
- 时序驱动布线器完成一次"低难度布线"通常比布通率驱动布线器快 约 10 倍
- 对最大的基准电路 clma(约 8381 个 LUT),时序驱动布线器只需几分钟就能完成布线
原因在于:
- 直接搜索(类似 A*)比宽度优先波前,更少无效扩展
- 动态成本 + 关键度引导,使得"真正影响时序"的连线优先被优化,早期就确定较优拓扑
七、时序分析与延时提取的工程实现
为了让上述算法跑得动,VPR 在延时提取和时序分析上也做了优化。
7.1 延时提取
在建立布线资源图时,对每个节点提前汇总:
- 总电容 Ctotal:合并所有相关开关电容 + 线电容
- 金属电阻 Rmetal
这样,在布线过程中就可以用 O(1) 的代价去访问这些参数。
对一棵布线树上的所有节点延时,可通过"两次深度优先遍历"在线性时间内完成:
- 自顶向下计算 Rupstream
- 自底向上计算每条边对全局延时的贡献
在每次为某条线网增加一个新分支时,只需要对受影响的那一小段树做增量更新。
7.2 时序分析
VPR 内置了一个基于路径的时序分析器,流程大致是:
- 读入网表 + 结构描述,构造时序图
- 对时序图做分级(拓扑排序)
- 在每次布线迭代之后:
- 用延时提取器把线网延时标注到时序图
- 前向遍历求到达时间,后向遍历求必需时间
- 得到每个连接的 slack,进而更新 (Crit(i,j)) 和 Dmax
这一步在 UltraSparc 300MHz 的工作站上,对 clma 这样的最大基准电路,整个过程(延时提取 + 构建时序图 + 分析)大约在 1s 级别。
八、与其他时序布线器的比较与结论
当时业界已有一些针对 FPGA 的时序驱动布线器,但普遍存在:
- 使用线性或 Penfield-Rubinstein 等模型,和真实 SPICE 延时有较大出入
- 结构单一(只用传输管 + 单元长线),不适用现代商用 FPGA 混合缓冲结构
本书的时序驱动布线器特点可以概括为:
- 直接优化 Elmore 延时,而不是用线性模型代替;
- 同时兼顾:
- 拥挤度(保证布通)
- 拓扑结构(避免长链传输管)
- 布线长度(减少资源浪费)
- 缓冲插入位置(改善时序)
- 在 MCNC 20 个最大电路上,和自己的布通率驱动布线器相比:
- 延时平均提升 2.6 倍
- 只多用 6%~7% 轨道
- CPU 时间反而少一个数量级
综合本章上下两部分,可以把 VPR 的布线部分总结成一句话:
用"参数化结构 + 自动图生成"保证结构层面的灵活性,用"拥挤度驱动 + Elmore 时序驱动"的双层布线器,在同一套框架下同时解决"能不能走得通"和"能跑多快"的问题。