做数字后端的同行大概率都碰到过这种场景:floorplan的macro刚摆好,place一跑,congestion map上一片深红,工具把标准单元像塞棉花一样挤在macro出pin的通道上,到了detail route阶段一堆DRC根本收不干净。这时候翻出脚本,加几块PR工具里的blockage,重跑一遍,地图颜色立马从深红降到黄绿。blockage这东西门槛不高,但真要用得顺手,得先想清楚它到底是在跟工具"谈判"什么——是硬性禁止,还是软性劝退,是限定某一层金属,还是只约束pin的摆放。这篇就把我在几个实际项目里攒下的blockage用法整理一遍,从原理到命令到踩坑,适合刚接触PR流程的新人,也适合做了几年但一直靠GUI手画blockage的老手对照看看有没有可以脚本化的地方。
1. 先弄明白blockage到底在解决什么问题
1.1 从一次congestion爆表说起
PR工具(Place and Route,布局布线工具)的默认行为是"能塞就塞",它的优化目标里面积和时序权重很高,只要DRC过得去、时序能收敛,工具并不在意某个通道将来会不会堵死。所以在macro密集的区域,工具会把standard cell铺到macro引脚出口旁边,global route一看,pin出来的net全挤在同一个track上,congestion直接爆表。blockage做的事情很朴素:在版图上画一块区域,告诉工具"这里你不要动"或者"这里你悠着点用"。
它解决的是一类非常具体的问题——工具缺乏全局视野。工具在place阶段只看到局部density和局部timing,它不知道某个通道将来要跑多少根net,也不知道某个区域上方是电源网格的密集via阵列。这些信息只有设计者知道,blockage就是把人的意图翻译成工具能理解的几何约束。理解了这一点,后面选hard还是soft、画在M2还是M4,判断标准就清晰了:你想传达的是"绝对不许",还是"尽量少"。
1.2 blockage和density约束不是一回事
很多人一开始会把blockage和placement density target混为一谈,觉得都是控制cell密度的手段,随便挑一个用就行。实际上两者的作用机制差别很大。density target是一个统计量,工具会把整个区域(或指定区域)的cell占用面积比例压到一个目标值以下,但它不指定具体哪块地方必须空着,工具完全可以把cell贴着macro摆,只要总面积达标就行。blockage则是几何硬约束,你画的那个矩形,工具就一个cell都不放(hard类型),或者只放到某个比例(partial类型)。
举个实际例子:一个macro阵列中间有一条200微米宽的横向通道,你希望这条通道用来走macro之间的信号线。如果只给这个区域设density 40%,工具仍然可能在通道两侧堆满cell,只留中间一小条缝;而画一条贯穿的hard placement blockage,通道就被完整保下来了。反过来,如果只是想整体缓解一下某个区域的拥塞,不希望出现大片空白影响filler填充,那soft blockage或者density target更合适。选择哪种手段,取决于你要保的是"一条具体的路"还是"一个区域的整体松紧度"。
1.3 主流PR工具里blockage的家族图谱
不同工具的叫法和分类略有差异,但核心概念是相通的。以常见的两类工具为例,blockage大致可以分成这么几个家族:
| 类型 | 作用对象 | 典型用途 | 是否可分层 |
|---|---|---|---|
| Placement Blockage | 标准单元/macro | 保住布线通道、隔离敏感模块 | 否 |
| Routing Blockage | 信号线(net) | 禁止绕线穿macro、预留电源通道 | 是 |
| Pin Blockage | macro/IO的引脚 | 阻止pin打在不需要的边上 | 是 |
| Via Blockage | via | 保护敏感区域不打via | 是 |
| Obstruction | 布线资源 | 为自定义图形预留空间 | 是 |
这里要特别说明一下:placement blockage和routing blockage是两个独立的层。画了placement blockage不等于布线也被挡住,工具照样可以让信号线从这块区域上方穿过去,只是下面没有cell而已。反过来,画了routing blockage,cell还是可以放在那里,只是net走不过去。实际项目里经常需要两个一起加,尤其是macro内部——既不想放cell,也不想走线。
另外还有一类容易被忽略的是跟macro绑定的halo(keepout margin)。halo是跟着macro实例走的,macro移动,halo跟着移动;而blockage是画在固定坐标上的,只有在工具支持"relative to macro"的写法时才跟着跑。这个区别在后期ECO挪macro的时候非常要命,后面第4节会专门讲。
2. 四类blockage的核心参数与实操要点
2.1 Placement Blockage:hard、soft、partial怎么选
Placement blockage的三种主要类型,行为差异比名字看起来大得多。
hard类型是最严格的,指定矩形范围内一个标准单元都不放,包括后来insertion阶段要填的filler、decap、tap cell。这一点新手最容易忽略:你画了一块hard blockage,跑完place发现利用率只有65%,report_density一看那块地方全空着,然后CMP(化学机械抛光)阶段的density uniformity检查过不了,因为大片空白会导致抛光不均。所以hard blockage一般只用在真正必须空的地方,比如analog模块上方、电源ring内部、或者顶层故意留的走线通道。
soft类型是"劝退",工具会尽量不往这里放cell,但实在没地方去的时候还是会放。它的实际效果取决于工具对该区域的density惩罚权重,不同版本行为不完全一致。soft blockage的好处是不会留下硬空白,坏处是效果不够确定。我的经验是,soft blockage适合用来"微调"congestion,比如某个区域红了但不严重,加个soft就能降下来。
partial类型是前两者的折中,你需要直接指定一个density上限,比如-density 30表示这块区域cell占用不超过30%。这个参数的好处是可控性强,你能精确算出留多少空间给布线。partial blockage在macro通道排布很规整的设计里特别好用,比如通道宽度固定、net数量可预估的场景,30%还是50%有个大致算法:大致留出(1-d)的比例给布线资源。
macro-only类型是另一条路:只阻止macro被放进来,standard cell照放不误。这个在macro array的通道规划里很有用——你希望通道里不要被后来加的macro堵住,但允许cell填进去提高利用率。
2.2 Routing Blockage:层次、密度与spacing的组合拳
Routing blockage(有些工具叫wiring blockage)的玩法比placement丰富得多,因为它可以按层指定。
最常见的用法是禁止绕线穿过macro内部。绝大多数macro的LEF里其实已经带了内部obstruction,工具知道macro内部不能走线,但问题是macro边缘那几十微米往往还是会被工具用来"蹭线",导致macro pin附近DRC密集。这时候在macro外围(比如pin所在的那条边往外扩5微米)加一圈hard routing blockage,情况会明显好转。
partial routing blockage则是指定某层金属的最大走线密度,比如-density 40表示该区域该层金属的track占用不超过40%。这个在电源网络规划里特别好用:有些设计电源stripe打得很密,工具不知道下面还有stripe,信号线又铺一层,结果via和metal叠在一起。加个partial blockage给信号线留出余量,power和signal就分开了。
spacing参数是个容易漏掉但很重要的细节。它表示blockage边界之外还要额外留出的禁区宽度。为什么要留?因为信号线如果恰好贴着blockage边缘走,旁边就是blockage内的另一根线(比如macro内部走线),两根线间距不够就会DRC。留个spacing,相当于给blockage"包了一层缓冲带"。我一般会按该层的最小间距规则再加20%来设,比如M3最小间距0.1微米,spacing就设0.12微米。
层次指定上要注意:不是所有层都需要加blockage。通常M1到M4是资源最紧张、最容易被macro影响的层,重点管这几层;M5以上是电源和长线通道,一般不太需要。全层加blockage反而会让工具无路可走,congestion更糟。
2.3 Pin Blockage和Via Blockage:被低估的两个小角色
Pin blockage(有些工具叫pin access blockage)用来限制工具在哪些区域打macro的pin。这听起来有点奇怪——pin的位置不是macro LEF定死的吗?实际上很多macro的pin在LEF里是可选的,工具会自己挑一个更利于布线的边。如果你不加限制,工具可能选了一条完全不适合的边,导致那条边的net绕一大圈。给不需要的方向加pin blockage,相当于帮工具做决定。
Via blockage则是保护敏感区域。比如某个区域上方要走一条关键的时钟线,你不希望任何via打在那里(via会增加电容,影响时钟质量),就可以加一块via blockage。另一个场景是OD(氧化物扩散)区域的应力敏感区,via带来的机械应力可能影响器件特性,加via blockage做保护。
这两个blockage平时用得少,但在特定场景下能救命。我在一个模拟混合数字的项目里,就靠pin blockage把macro的pin从模拟侧强行赶到数字侧,省了将近15%的绕线长度。用不用这两个,取决于你的设计里有没有"特定方向""特定层"的敏感需求。
2.4 参数设置里那些一填就错的坑
不管哪种blockage,有几个参数是反复出错的重灾区。
坐标单位是第一大坑。PR工具的数据库单位(database unit)通常是1微米等于1000个整数单位,你写坐标的时候如果从GUI上抄了小数点的微米值直接填进脚本,blockage就会跑到die外面去。正确做法是统一换算:微米值乘以1000。比如macro从(10.5, 20.3)到(110.5, 120.3),单位是微米,脚本里就得写{10500 20300 110500 120300}。
图层命名是第二大坑。不同工具、不同工艺库对同一层的叫法可能不一样,M1、MET1、metal1、ME1都见过。写脚本前一定要用工具命令(比如列出所有可布线层的命令)确认一遍,否则工具要么报错要么默认给了别的层,你还以为加上了。
blockage的作用域是第三大坑。blockage是加在design顶层还是加在某个module内部,效果完全不同。加在module内部,当这个module被例化多次时,blockage会跟着复制多份;加在顶层,只有一份。多数情况下我们希望的是顶层一份,但在做可复用block的IP时,加在module内部反而更合适。
重叠blockage是第四个大坑,虽然不影响功能,但会让DEF文件膨胀、工具处理变慢,而且后期维护时根本分不清哪块是谁加的。我的习惯是每次加完blockage都跑一遍合并检查,重叠的合并成一块。
3. 从floorplan到routing:blockage的完整实操流程
3.1 前置准备:坐标、单位与层次命名
动手之前,有三件事必须先确认清楚,否则后面全是返工。
第一,确认数据单位。打开工具的report信息,找到database unit和technology unit。绝大多数先进工艺是1微米=1000单位,但也有例外。确认之后,后续所有坐标换算都用这个系数。
第二,导出一份macro的准确坐标清单。别从GUI上目测抄,用工具命令把每个macro实例的bounding box打印出来,存成文本。这样后面写脚本时直接引用,避免抄错。
第三,列出所有可用的布线层名。用report_layer之类命令或者查技术文件,把M1到顶层金属的准确名字记下来。这一步花五分钟,能省掉后面调半天脚本找不到层的痛苦。
3.2 创建blockage的具体命令与坐标系换算
下面给出常见工具的命令写法,注意版本不同语法可能有差异,以你手上工具的手册为准。
Innovus风格的写法:
# 在macro u_core/u_mem 周边加一圈hard placement blockage createPlaceBlockage -name pb_mem_halo -type hard \ -box {10500 20300 110500 120300} # 一块soft blockage,用于缓解congestion createPlaceBlockage -name pb_soft_1 -type soft \ -box {200000 150000 260000 210000} -pushdown # 禁止M2到M4穿过macro内部 createRouteBlk -name rb_mem_inner -layer {M2 M3 M4} \ -box {10500 20300 110500 120300} # 带spacing的routing blockage,边界外留0.12微米缓冲区 createRouteBlk -name rb_chan -layer M3 \ -box {200000 150000 260000 210000} -spacing 120ICC2风格的写法:
# 创建placement blockage create_placement_blockage -boundary {{10.5 20.3} {110.5 120.3}} -type hard # 创建routing blockage,指定层 create_routing_blockage -boundary {{10.5 20.3} {110.5 120.3}} \ -layers {M1 M2 M3} # 创建pin blockage create_pin_blockage -boundary {{10.5 20.3} {110.5 20.3}} -layers {M2}写脚本的时候有个实用技巧:用变量存坐标,别到处硬编码数字。比如你把每个macro的位置定义成变量,blockage的坐标由macro坐标加偏移算出来,这样macro挪了,blockage跟着改一行就行。这个习惯在迭代到第三四轮的时候价值巨大。
3.3 效果验证:怎么确认blockage真的生效了
加完blockage不能直接跑place就完事,得先验证。验证分三步。
第一步,图形验证。在GUI里打开blockage的显示层,把placement blockage和routing blockage分别显示出来,对着macro和die边界看一眼,位置对不对、有没有跑到边界外、有没有意外的重叠。这一步能抓出90%的坐标错误。
第二步,行为验证。跑一个快速的placement试算,然后看结果:hard placement blockage区域内应该是空的;partial blockage区域内density应该接近设定值;routing blockage区域内的net应该绕开。用工具的报告或者density map都能确认。
第三步,约束检查。有些工具提供专门的检查命令,能报告blockage和macro、IO、power ring之间的冲突。比如blockage覆盖了某个power stripe的位置,工具会警告,这个警告必须处理,不然电源网络会被打乱。
三步行下来,确认无误再进正式的place流程。
3.4 迭代维护:ECO阶段怎么管理blockage
项目做到中后期,blockage的增删会变得非常频繁,这时候如果没有管理方法,很容易乱套。
我的做法是三层结构:base层是floorplan阶段加的、稳定的blockage,写在主脚本里;tune层是place之后根据congestion调出来的,单独一个文件;eco层是route阶段针对具体DRC加的临时blockage,再单独一个文件。每次改动都记在变更日志里,写清楚时间、原因、谁加的、加在哪。这样出了问题能快速回溯,也不会出现"这块blockage到底还有没有用"的困惑。
还有个实际经验:能脚本化就别用GUI手画。GUI画的blockage在DEF里没有名字,后期想删都不知道删哪块;脚本创建的blockage带名字,可以用名字精确删除和修改。项目越到后期,这个差别越明显。
4. 常见问题排查与踩坑实录
4.1 问题速查表
先把最常见的几类问题列成表,方便快速定位。
| 现象 | 可能原因 | 处理方向 |
|---|---|---|
| blockage加了但区域内仍有cell | 加成了soft类型或作用域不对 | 改hard类型,检查是顶层还是module内 |
| blockage跑到die外面 | 坐标单位错,微米值没乘1000 | 重新换算坐标 |
| 脚本报找不到层 | 层名拼写与工艺库不一致 | 列出可用层名,统一命名 |
| place后利用率异常低 | hard blockage过多,留白太大 | 部分改soft或partial |
| 布线绕远、总长度增加 | routing blockage范围过大或层数过多 | 缩小范围,减少层数 |
| DEF文件异常大 | blockage重叠冗余未合并 | 跑合并检查,清理重叠 |
这张表覆盖了我在项目里见过的八成问题。剩下的两成通常跟具体工艺有关,需要结合DRC报告看。
4.2 五个真实踩坑案例
案例一:hard blockage把filler挡在门外。有个项目在analog模块上方加了整片hard placement blockage,place跑完看着挺好,等到filler insertion阶段发现那片区域利用率只有58%,density uniformity检查直接报错。后来把hard改成partial 60%,问题解决。教训是:hard blockage要想清楚后期filler怎么办。
案例二:macro内部没加routing blockage,线穿堂而过。一个项目macro的LEF里obstruction定义不完整,工具认为macro内部某几层可以走线,结果global route把十几根net导进macro内部,detail route阶段一堆DRC。补加routing blockage之后解决。教训是:不要完全信任macro LEF,关键macro建议手动加routing blockage兜底。
案例三:坐标单位搞错,blockage集体移位。新手同事从GUI上抄了微米值直接写进脚本,结果一堆blockage集中在die的右下角一小块区域,其余地方该堵的没堵。debug了一下午才找到原因。教训是:换算这一步必须有,而且要写注释说明换算系数。
案例四:spacing设太大,把宝贵通道也堵死了。为了防DRC,某块blockage的spacing设成了0.5微米,本来通道就窄,一留缓冲区剩下的走线空间更少,congestion反而更严重。后来把spacing降到0.12微米,配合partial blockage,情况好转。教训是:spacing要按最小间距规则来算,不是越大越好。
案例五:blockage重叠没合并,DEF膨胀。项目后期为了赶进度,多人往同一区域加blockage,DEF里出现了大量互相重叠的矩形,文件从几兆涨到几十兆,工具跑一步要等半天。清理合并之后恢复正常。教训是:多人协作必须有blockage的登记机制。
4.3 和congestion map配合的调优思路
blockage不是万能药,它只能改变工具的"地图偏好",不能凭空造出布线资源。所以调优的正确姿势是先看congestion map,再决定加什么blockage。
具体流程:跑完global route,打开congestion map,找到hotspot。然后判断hotspot的成因——如果是macro pin太密导致局部溢出,加pin blockage把pin分流到别的边;如果是通道不够宽导致整体拥塞,加placement blockage把cell赶出去,腾出通道;如果是电源网格占用太多track,调整电源方案或者给信号线加partial routing blockage。不同成因对应不同手段,乱加blockage只会按下葫芦浮起瓢。
还有一个容易被忽略的点:blockage加完之后要重新看congestion map,确认hotspot真的降下来了,而不是被推到别的地方去了。有些时候你把cell赶出一块区域,它们全挤到旁边,旁边的hotspot反而更严重。这种情况下需要的是配合调整floorplan,而不是继续加blockage。
5. 关于blockage的一些个人经验
做了这些年,我对blockage最大的体会是:它本质上是人和工具之间的一种妥协。工具不懂设计意图,你只能用几何图形去表达。表达得越精确,工具干得越符合预期;表达得越随意,后期返工越多。
我现在的习惯是每个项目开工前,先花半天时间把所有macro的halo、通道、敏感区域梳理一遍,写成一份blockage规划表,跟着floorplan一起定下来。这份表在place、cts、route三个阶段都会用到,每次迭代只做微调,不做大改。这样虽然前期慢一点,但整个流程下来省的时间远超投入。
另外一个小技巧:给每个blockage起有意义的名字,比如pb_mem_u_core_top、rb_chan_m3_horizontal这类,一眼能看出是placement还是routing、管的是哪个模块、在哪一层。后期维护的时候,名字就是最好的文档。这个习惯我是从一个老工程师那里学来的,用了几次之后就再也没改回去过。
最后提一句,blockage的量和设计的复杂度是正相关的。一个几十万门的小设计,可能几块blockage就够了;上千万门的SoC,blockage文件动辄上千行。所以脚本化和版本管理在小项目里是锦上添花,在大项目里是生死攸关。早一点建好这套流程,后期会轻松很多。