1. 布图规划到底在做什么:一个被低估的起点
刚接触数字后端的朋友,往往会把大部分注意力放在布局布线(Place & Route)上,觉得把标准单元放下去、把线绕通就是核心工作。但真正做过几个项目之后,你会发现整个芯片的成败,很大程度在布图规划(Floorplan)这一步就已经定下来了。Floorplan 做得顺手,后面的布局布线、时钟树综合、时序收敛都会轻松很多;Floorplan 做得潦草,后面所有环节都在替这一步的错误买单。
布图规划,简单来说就是把芯片的物理空间先“分好地盘”。芯片有多大、核心区域放在哪里、IO 怎么排、大的宏单元(Memory、IP)摆在什么位置、电源网络怎么铺,这些都是 Floorplan 阶段要定下来的事。它做的是“宏观布局”,而不是标准单元级别的“微观布局”。你可以把它理解成:装修之前先确定哪里是客厅、哪里是厨房、哪里是卧室,水管电线的走向也提前规划好,后面再往每个房间里摆家具、走线路——家具和线路可以调整,但墙的位置和主水管走向如果一开始就定错了,返工代价极大。
在 Innovus、ICC2 这类主流数字后端工具里,Floorplan 是进入后端流程时遇到的第一道正式关卡。输入是一堆约束文件和设计数据,输出则是一个把逻辑设计映射到物理空间上的版图雏形。这个雏形可能看起来只有几个框框和线条,一点都不“芯片”,但它决定了整个后续流程的可实施性。这篇文章我就从实际项目操作的角度,把布图规划中那些容易忽略、但非常关键的知识点和踩坑经验梳理一遍,既适合刚入门数字 IC 后端的新手,也适合已经跑过流程但想回头补一补底层逻辑的工程师。
2. 开工之前,先捋清楚:布图规划需要哪些输入,输出又是什么
2.1 输入数据:不是拿到 Netlist 就能直接动手
很多新手第一次做 Floorplan 的时候,会比较困惑:工具里明明只有一个空空的版图界面,怎么才能把设计“放进去”?其实在打开工具之前,需要准备的材料远比你想象的多。标准的一套输入大概包括下面几类:
- 门级网表(Gate-level Netlist):综合后的网表,记录着所有标准单元和宏单元的逻辑连接关系。这是 Floorplan 的“内容物”,你规划的空间最终都是要装下这些东西的。
- 工艺库文件(Liberty / .lib):包含标准单元、宏单元的时序、功耗、面积等信息。工具需要靠它来了解每个单元的形状、引脚位置、时序特性。
- 物理库文件(LEF / .lef):LEF 文件记录的是单元的物理信息,比如单元的高度、宽度、金属层形状、pin 的位置等。抽象版的 LEF 只有边界和 pin,详细版则带有布线阻挡等信息。Innovus 在导入设计时,就把这些物理库加载进来了。
- 技术文件(Techfile / .tf):定义工艺的金属层数、线宽、间距、通孔规则、布线网格等。这个文件决定了你后面布线时的物理规则。
- 约束文件(SDC):时序约束,虽然 Floorplan 阶段不像布线阶段那样严格跑时序,但 SDC 中的时钟定义会影响时钟树规划、IO 时序预算分配。
- IO 信息文件(可选):如果芯片有封装约束,如 IO 位置、焊盘排列、供电 IO 的种类,通常需要单独提供,可能是
.io文件或由封装团队给出的 pin 位置定义。
如果你用的是 Innovus,还需要关注初始化阶段的命令,比如init_design或init_floorplan。在比较新的版本里,Innovus 支持通过读入 LEF、DEF、netlist、SDC 等数据来初始化设计,然后再开始做 Floorplan。这里我补充一个常见误区:很多人以为只要有了 netlist 就能开始布图规划,其实没有物理库(LEF)和技术文件(Techfile),工具里看到的只是抽象的逻辑连接,根本无法判断单元摆下去会不会冲突、布线资源够不够。所以拿到一个项目后,我习惯先把所有输入文件整理成一个清单,逐一确认。
2.2 输出物:Floorplan 到底“产出了什么”
布图规划阶段的输出,通常包括下面几项:
- 芯片尺寸与核心区域(Die size & Core area):确定了整个芯片的边界、核心区域(core boundary)的位置和大小、IO 区域的宽度。
- IO pin 或 IO pad 的摆放:如果设计带 pad,需要把数十乃至数百个 pad 一一摆好;如果是纯 core-limited 设计,则确定 pin 的位置。
- 宏单元的初步/最终摆放:根据设计要求和数据流,把 memory、模拟 IP、特殊功能模块放到合适位置,并设置好 keepout、halo 等约束。
- 电源网络规划:包括 power ring、power stripe、电源 IO 的连接方式等,这部分有时会单独划分为电源规划(Power Planning),但通常和 Floorplan 一起做。
- 标准单元区域划分:比如划分出数字逻辑区、模拟区、高速接口区等,也可能用 region 约束固定某些模块的摆放范围。
- 各种约束文件:最终输出 DEF 文件、floorplan 配置文件等,供后端流程下一步使用。
很多后端项目里,Floorplan 会和电源规划一起做,因为电源网络要在宏单元摆放定下来之后才能合理铺设。实际操作中,我会在 Innovus 里把 Floorplan 和 Power Plan 一块调,这样少开一次工具、少导入导出一次数据,对项目节奏紧凑的场景非常重要。
3. 芯片尺寸估算与 Core 利用率:别小看这一步的数学题
3.1 面积估算:照着网表和库动手算
布图规划的第一步,一般是从估算芯片尺寸开始的。芯片不能想定多大就定多大——做大了浪费晶圆成本,做小了放不下或者布线拥塞严重。Core 区域的大小,主要由标准单元总面积 + 宏单元面积 + 布线资源预留 + 电源网络占用这几个部分组成。
标准单元总面积的估算方法是:
标准单元总面积 ≈ 标准单元数量 × 平均单元面积
实际项目中,更严谨一点的方法是直接读网表,把每个实例(instance)的面积加起来。Innovus 的report_design或者读入设计后的报告里,会给出总的 cell area。但这里要注意,这个面积是所有单元面积之和,Layout 里各个单元之间是会留缝隙和布线通道的,所以真正需要的面积要比这个值大得多。
这时候就需要引入一个概念:利用率(Utilization)。它的定义大致是:
Utilization = 单元总面积 / Core 区域面积
如果 utilization 是 70%,意味着核心区域里有 70% 被单元占据,剩下 30% 用于布线、电源网络、单元之间的空隙。这个值没有绝对标准,通常在 60%~75% 之间比较常见。如果设计里有很多比例很大的宏单元,或者高速接口很多、布线资源消耗大,利用率就要适当调低;如果是一个很规整的数字逻辑设计,利用率可以稍微高一点。
我见过不少刚入行的同事,拿到网表后直接把report_utilization里的数字当成最终结果,完全不考虑宏单元和布线资源。实际上工具在还没有 Floorplan 的时候,给你报的 utilization 一般只是标准单元面积与某个默认区域的比值,参考意义有限。真正靠谱的做法是:先用宏单元面积 + 预估标准单元面积算出总需求,再根据经验留出 20%~40% 的余量,然后反推 core 尺寸。这样做出来的 Floorplan 才不会在布线阶段处处碰壁。
3.2 从 Core 到 Die:IO 和外围空间也要算进去
Core 区域确定之后,还要加上 IO 区域、电源环、ESD、密封环(seal ring)等外围空间,才能得到最终的 die size。IO 区域到底留多宽,取决于用的 IO cell 类型和数量。如果是普通数字 IO,每个 IO cell 的宽度是工艺库里写好的;如果设计里还有模拟 IO、电源 IO,它们的宽度往往和普通数字 IO 不一样,需要单独核算。
这里分享一个我用过很多次的经验:不要只看 IO cell 本身的宽度,还要看你的封装方式。如果是 Flip-chip 设计,IO 可能不是围着四周排一圈,而是分布在芯片表面的凸点(bump)阵列上,这时候 die size 会受到 bump 间距和 IO 数量的影响,计算方式完全不同。如果是 Wire-bond 封装,四边 pad 排布方式的面积计算就相对直接。所以在估算尺寸之前,先搞清楚封装方案,是我一直坚持的做法。
3.3 行高、Site、Track 与金属层方向:底层规则决定布局效率
在 Innovus 里做 Floorplan 的时候,你会发现工具要求你指定 core 边界时,会提示“snap to track”或者“snap to site”。这些底层的网格概念如果不理解,做出来的 core 边界可能和标准单元的 row 对不齐,轻则浪费面积,重则导致单元摆不进去。
简单来说,标准单元库里每个单元的高度都是固定值,一般是某个 base height 的整数倍,这个高度单位叫site。单元按行(row)排列,行与行之间的距离就是 row height。Core 区域的边界最好落在整数倍的行高上,不然行排不满,边缘就浪费了。同样,核心区域的宽度最好和布线网格(track)对齐,这样后续电源网络和布线才能顺畅。
金属层方向也是一个关键设计决策。CMOS 工艺里,一般奇数金属层走水平方向(Horizontal),偶数金属层走垂直方向(Vertical),比如 M1 横、M2 纵、M3 横、M4 纵。标准单元内部的 pin 基本都从 M1 引出,所以 M1 方向是横的时候,单元的行方向往往也是横向排列。这些都是 Floorplan 阶段就要确认的规则,如果一开始 core 边界没有对准 site 和 track,后面布线阶段会不断冒出 DRC 违例或者绕线困难。
还有一个容易被忽略的点:row height 与双倍高单元(double-height cell)。现在工艺节点越来越先进,很多库里有各种高度的单元,比如 high-performance 单元或者带多个 pin 的特殊单元。这些单元摆放时对 row 的对齐要求更严格。做 Floorplan 时如果 core 高度没有按行高的整数倍设计,后期加 high-density 单元时很可能出现区域放不下的尴尬局面。
4. IO 规划与 Pin 布置:先看封装,再看信号
4.1 封装决定 IO 位置:别把顺序搞反了
IO 规划这件事,很多设计初学者会从“电路功能”的角度去考虑:把相关的信号放在相邻的位置,这样逻辑上顺一些。这个想法不能说全错,但在真实项目里,IO 位置的第一决定因素是封装。封装团队会根据 PCB 布局、引脚间距、电源完整性等要求,给出芯片 IO 的初步排布建议,这份建议通常就是硬约束——后端设计需要照着它来做。
我之前做过一个带 DDR 接口的设计,DDR 的信号要求等长、分组摆放,且每组信号有自己对应的电源地 pin,这些要求全部来自封装方案。如果 Floorplan 阶段没有和封装团队对清楚,随意摆放 DDR 的 IO,之后绕线阶段会发现这些信号无论如何都绕不到合理的长度和层数——不是绕不通,而是物理上离各自对应的 IO pin 太远了。等到项目后期再调 IO,往往是牵一发而动全身,得不偿失。
4.2 IO 排布的细节:Power IO、Signal IO 和位置约束
在 Innovus 里编辑 IO 位置,可以使用editFPlan界面手动拖动,也可以直接编辑 io 文件然后加载。对于大型设计,手动拖动不现实,一般会先让工具根据某个策略自动排一遍,再手动微调。这里有几个细节值得关注:
- 电源地 IO 要均匀分布:电源地 IO 不能全部挤在一个角落,否则芯片内部的电源网络离电源 IO 太远的地方会有严重的 IR Drop。所以你可以看到很多设计里,VDD/VSS IO 是呈间隔分布的,比如每 8 个信号 IO 配一对电源地 IO。
- 高速信号优先:高速接口(如 SerDes、DDR)的 IO 尽量靠近芯片边缘且位置对称,减少信号路径长度差异。某些信号对还有差分约束,IO 位置需要成对出现。
- 时钟信号 IO 要特别关注:时钟信号通常需要经过专用的时钟树网络,IO 位置离内部时钟源不能太远,否则时钟树综合的难度会直线上升。
- 可测性设计(DFT)引脚:扫描链、测试时钟等引脚的位置一般也有要求,尤其是与测试模式相关的时序约束,需要提前和后端负责人、DFT 工程师核对。
IO 规划完成后,建议在工具里把 IO 的位置、名称、对应的 net 都逐一过一遍。我吃过一次亏:某个信号 IO 被自动工具排到了对角位置,和内部模块距离极远,布线阶段那根线绕了整整三层金属还超长,时序修了很久。后来再做 Floorplan,我一定会在 start point 阶段就把关键 net 的 IO 位置手动约束好,不让工具自由发挥。
5. 宏单元摆放:布图规划真正的核心难点
5.1 摆宏单元之前,先问三个问题
宏单元(Macro,主要是 Memory 和大 IP)摆放是整个 Floorplan 中最考验经验的部分。很多初学者跑到 Innovus 里把宏单元扔进去,然后点自动放置,发现工具给了个看起来挺整齐的布局,就以为大功告成——结果后面布线阶段全是坑。摆宏单元之前,我建议先问自己三个问题:
一,数据流是什么方向?如果设计中 A 模块的数据经过这块 Memory 再传给 B 模块,Memory 放在 A 和 B 之间就是最自然的选择。如果多个 Memory 之间有数据交互,它们应该形成一个簇,让连线尽量短。
二,宏单元和标准单元区之间需要多大间距?宏单元附近通常需要留出一圈 halo(也叫 keepout margin),用来避免标准单元和宏单元贴得太近导致布线困难。Halo 的大小没有固定值,一般取决于宏单元的 pin 密度和金属层使用情况,常用的值在 5~20 微米不等。注意,halo 如果太小,宏单元周围的绕线资源会很紧张;halo 如果太大,又浪费面积。
三,宏单元的朝向(orientation)怎么选?宏单元内部有大量 pin 和内部走线,朝向不同会导致 pin 方向和相邻区域的布线资源分布不同。一条经验是:宏单元 pin 密集的一侧应该朝向信号到来的方向,pin 相对稀疏的一侧可以和别的宏单元背靠背相邻摆放。
5.2 数据流向与宏单元分组:照着数据流摆,比盲自动省心
我之前做过一个 SoC 项目,里面有一块大内存阵列,和 CPU、DMA、外设控制器都有数据交互。如果单纯依赖工具的自动放置,它可能会把这块 Memory 放在一个“看起来面积利用率高”的位置,但那一侧的 IO 方向、时钟资源、布线压力全都不是最优的。当时我的做法是:
- 先从架构图出发,把每个宏模块的数据流向画出来,标注出和哪个模块通信最频繁。
- 把这些高频通信的模块尽量放到同一侧或相邻区域。
- 对于通信量较小的连接,哪怕远一点也能接受,这样能给核心数据流让出更短的路径。
这样人工初版摆放完成后,再交给工具做自动布局微调(placeDesign前处理阶段),效率会高很多。工具自动摆放并不是没用,而是它没有架构层面的全局观。我的习惯是:先人工定大框架,再用工具优化细节,两边结合才是稳妥路径。
5.3 宏单元与标准单元区域的间距:Halo、Keepout 与 Fence
在 Innovus 里,摆好宏单元之后,一般要给它设置约束,避免标准单元“侵入”宏单元周围的空间。常见的设置方式有:
- Halo:在宏单元外圈留出的空白区域,标准单元不能进入。
- Keepout Margin:一种更灵活的禁止区域定义,可以按距离或按层来设置。
- Fence/Region:强制指定某个模块的单元只能放在某个区域内(fence),或者只允许放在某个区域内(region)。
实际项目中,宏单元多的时候我会对每个宏单元设置 halo,同时给某些关键的模块设置 region,比如把 CPU 核心放在一起,不允许它扩散到 Memory 堆里去。还要注意,halo 并不是设得越大越好。我有一次为了图省心,把 Memory 的 halo 设得特别大,结果是标准单元能用的区域变得碎片化,利用率下降,后续布局时单元只能分散在小块区域里,反而增加了绕线长度。平衡面积和可布线性,永远是 Floorplan 阶段不断权衡的主线之一。
6. 电源规划与布图规划:两件事经常一起干
6.1 电源网络的骨架:Power Ring 与 Power Stripe
布线阶段真正绕信号线之前,得先把电源网络搭好。电源网络其实和城市供电系统很像:CORE 区域是一个城市,电源 IO 是发电厂,power ring 是城市外环线,power stripe 是主干道,标准单元的 VDD/VSS pin 是每户人家的电表。这些“电力主干道”如果不提前规划好,等所有信号线都绕完了再补电源线,几乎不可能。
Power Ring(电源环)包围在 core 周围,通常由 VDD 和 VSS 两条金属环组成,有时候还有多层金属叠加的环。环的宽度和层数取决于整个芯片的电流大小和 IR Drop 预算。Power Stripe(电源条带)则是在 core 内部横向或纵向铺设的长条形电源线,它们把电源从 ring 引入到内部,让每个标准单元都能就近取电。
6.2 电源规划参数怎么定:从电流需求反推
确定 Power Stripe 的宽度和间距,核心依据是电流负载和IR Drop 要求。假设某段电源线要承载 500mA 电流,工艺库给了每微米宽度金属的电流密度上限,比如 1mA/um,那么这一段线至少需要 500um 宽。当然实际不会这么死板,因为还有多层并联、通孔(via)的额外电阻、温度影响等因素。大多数项目会先按经验设定一个初始值——比如 M4/M5 每隔几百微米铺一条宽度为 core 宽度百分之几的 stripe——然后跑 IR Drop 分析,根据结果再迭代。
Innovus 里做电源规划的常用命令是addRing和addStripe。比如常见的命令形式大致是:
addRing -nets {VDD VSS} -layer {top M5 bottom M5 left M6 right M6} \ -width 10 -spacing 1 -offset 5 addStripe -nets {VDD VSS} -layer M5 -direction horizontal \ -width 8 -spacing 10 -set_to_set_distance 100需要注意,命令参数在不同版本和不同工艺库里差异挺大,最重要是确认三件事:用哪些金属层、方向和间距是多少、stripe 是否要避开宏单元区域或者特定区域。比如 Memory 上面有 MT 层过孔遮挡的时候,stripe 直接穿过去可能导致后续打孔冲突,这就要用-skip_via或绕开区域的设置来处理。
6.3 电源规划常见问题:Macro 下面的电源怎么接
宏单元自己的电源连接,是电源规划里非常容易踩坑的地方。很多 Memory 或模拟 IP 会在自己的边界上留出一圈 power pin,需要通过addRing或connectPower的方式连接到全局电源网络上。如果一个宏单元的电源连接方式不对,轻则 IR Drop 超约束,重则 LVS 都跑不过。
还有一种常见情况是宏单元的某些电源 pin 在顶层金属(比如 AP 层),普通电源 stripe 在 M5/M6,这时候需要通过特定通孔阵列连接。做 Floorplan 时就要把这些通孔的位置和占用面积考虑进去,不然等你要在它上方走线的时候才发现没有足够空间打孔,返工成本极大。
我之前负责一个带多个模拟 IP 的设计,有些 IP 要求特定的电源环宽度和 spacing,和数字区域的电源网络风格完全不一样。后来踩了坑才明白,宏单元的电源连接方式必须逐块确认,不能天真地认为统一铺一层 stripe 就万事大吉。
7. 布图规划阶段常见问题与排查方法
我在实际项目里把 Floorplan 阶段出过的问题大致列了一张速查表,分享出来供参考:
| 现象 | 可能原因 | 排查思路与调整建议 |
|---|---|---|
| 布局布线阶段标准单元利用率突然升高 | Core 区域过小或宏单元摆放不合理 | 检查宏单元总面积占比、halo 是否过大,适当扩大核心区域 |
| 某个区域内布线严重拥塞 | 宏单元 pin 过于集中、stripe 阻挡、region 设置不合理 | 查看 congestion map,调整宏单元朝向或为宏单元留出更多绕线通道 |
| IR Drop 在某个角落超标 | 电源 stripe 布设不均、宏单元下方电源连接不足 | 增加该区域 stripe 密度,检查宏单元 power via 数量 |
| 时序收敛困难,关键路径绕线过长 | 关键模块之间距离太远、数据流方向不合理 | 回看 Floorplan 数据流方向,将高频通信模块拉近 |
| 电源/地噪声导致功能异常 | 去耦电容缺失或分布不均 | 在 Floorplan 阶段预留 decap 填充区域,后补密集 decap |
| 标准单元无法铺满角落 | Core 边界没有对齐 site/track | 重新调整 core 坐标,使其对齐 row 和 track |
| IO 绕线特别长 | IO 位置远离相关模块 | 调整 IO 位置或通过 pin swap 优化关键信号 |
这些问题的共同点是:它们的根子往往不在出现问题的阶段,而在 Floorplan 阶段。这也是为什么我一直建议把更多时间花在布图规划上。后面步骤里你拼命调优化器参数、加约束修时序,可能效果远不如在最开始把几个宏单元的位置挪对来得好。
这里再提一个我常用的经验:Floorplan 做完之后,不要急着往后跑,先在工具里出一张 congestion map。Innovus 里有全局布线预估的功能,能在详细布线之前告诉你哪些地方大概率会拥塞。如果这时候发现热点区域,调整宏单元位置或 core 边界还来得及;一旦进入详细布线再改 Floorplan,时间成本至少翻倍。
8. 我做布图规划时的几个习惯性动作
最后分享一点我个人积累的小习惯,不一定适用于所有项目,但对大多数场景都有帮助。
第一,开始做 Floorplan 前,先把整个设计“看一遍”。我用 Innovus 比较多,导入设计后会把所有 net、instance、macro 按层次结构列出来,看看有没有模块面积特别大、pin 数量特别多、时序约束特别紧的地方,心里有个底。盲目直接画框子摆宏单元,等于闭着眼睛装修。
第二,宏单元摆完之后,用几行命令快速检查 pin 分布。如果某个宏单元一侧的 pin 特别密集,而这侧朝向了一片空旷区域,多半是合理的;如果 pin 密集侧紧贴着另一个同样 pin 密集的宏单元,大概率后面会绕线困难。这时候我一般会手动旋转宏单元或调整间距。
第三,不要舍不得调整。很多工程师做完了 Floorplan 就想着赶紧进入下一阶段,不敢动已经摆好的宏单元。但实际上,只要后面还没开始详细布线,改 Floorplan 的成本都不算高。我做过一个项目,在布局阶段发现一个宏单元的位置造成了严重的拥塞,虽然重新摆它花了一天时间,但后续绕线省了差不多一周的迭代。该改的时候果断改,比硬撑到最后再返工强得多。
第四,把 Floorplan 版本记录做好。Floorplan 的参数和约束修改非常频繁,每次改动都涉及宏单元位置、IO 顺序、电源网络等。如果没有版本管理,几天后你根本不知道当前版本和上个版本差在哪里。我的习惯是每完成一个稳定版本就导出 DEF 并备注改动摘要,和前端、封装团队同步时也能拿着版本号对需求。
数字后端是一条流程很长的链路,布图规划只是第一步,但它是决定整条链路走向的关键一步。把这一步做扎实了,后面每一步都能更顺;这一步偷懒,后面就会用更惨烈的方式还回来。希望这篇笔记对刚开始接触数字后端的朋友有帮助,也欢迎有经验的朋友分享你们在布图规划阶段的心得和踩坑记录,相互交流才能进步得更快。