ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Innovus中mem/macro摆放:数据流驱动的floorplan实战指南

Innovus中mem/macro摆放:数据流驱动的floorplan实战指南 在Innovus里做数字后端最让人头疼的往往不是时序本身而是“东西没放好”。尤其当设计里的mem/macro多到几十上百个时摆放顺序和位置基本决定了后面的布线能不能走通。这两天又处理了一个mem特别多的block趁热把思路和命令整理成一篇笔记希望对还在跟macro做斗争的朋友有点帮助。先交代一下背景这是一个偏SoC的模块里面大大小小的SRAM、register file加起来接近百颗逻辑里还有一堆需要靠近存储阵列放的定制单元。最初版本“凭感觉”摆完之后前几版布线结果非常难看局部congestion爆红关键路径绕线严重。后来老老实实按数据流重新做floorplan整套设计才慢慢收敛。这里说的“数据流”不是玄学而是真正能对应到网表连接关系的物理分布逻辑。1. 为什么mem一多摆放就必须跟着数据流走1.1 mem太多时“拍脑袋摆放”会引发什么问题很多人觉得floorplan嘛无非就是给大模块画几个框把memory往边上一放中间留空给标准单元。单个mem这么做没问题但mem一多问题就接踵而来。第一个问题是拥塞。memory本身有大量pin而且多层走线往往要从它们上方或旁边穿过。如果mem的位置和实际信号连接方向是拧着的比如源端在左下、负载端在右上中间却挡了一排memory后面布线就要花很大力气绕绕出来就是一片一片的congestion热点。这种热点在CTS之后会变成时序修复的噩梦修setup要插buffer插完buffer又引入新的density问题反复迭代到崩溃。第二个问题是不可控的长线。mem之间的数据总线通常非常宽几十上百根线同时从A模块连到B模块。如果两个模块因为mem摆得不合理被拉得很远这一大捆总线的延迟和绕线资源消耗会直接把时序打趴。而且总线绕线还有对齐要求绕多了DRC也容易爆。第三个问题是时钟树和复位树。memory的clock pin数量多、负载大通常需要单独处理。如果同一时钟域下的一批mem被拆到四个角落CTS阶段你要么插一堆buffer要么看着skew头疼。有些mem还有特殊clock gate要求位置不对后面物理综合都不好做。所以mem一多“拍脑袋”的随机摆放等于给自己挖坑。关键不是把每个mem放“好看”而是让它们在物理上尽可能靠近它们对应的逻辑让主要数据流的路程最短、绕行最少。1.2 数据流到底该怎么读才算读懂了“数据流”听起来抽象但在物理设计阶段它其实就是一句话信号从哪个方向进来经过哪些模块最后从哪个方向出去。放在网表里就是instance之间的连接关系。我的习惯是打开Innovus GUI把flyline飞线显示打开。这时候屏幕上每一根线都代表一个net连接密密麻麻一片但仔细看你很快能看出几条“粗管子”。那些连接特别多的区域就是数据流的主干道。比如一个模块有二十根128bit总线连到某个SRAM那这个SRAM就别想跑远它必须贴在对应逻辑边上。除了看飞线还要看连接密度矩阵。Innovus有相关命令可以报告instance之间的connectivity也可以自己在Tcl里用dbGet扫一遍遍历所有mem统计每条mem的net连到哪些逻辑模块按连接数量排序。这样能找出每个mem的“铁哥们”。物理上把这些“铁哥们”放在一起数据流就顺了。还有一个容易被忽略的点要看输入端口和输出端口的位置。Floorplan的IO pin通常决定了数据从哪进、从哪出。数据流的主方向大致就是“输入pin区 - 处理逻辑 - 输出pin区”。mem作为中间存储节点要么靠近输入侧做缓冲要么靠近处理逻辑做中间结果缓存要么靠近输出侧做输出缓冲。先定好主方向再细化每个mem的位置比东放一个西放一个靠谱得多。1.3 读懂数据流之后怎么转化成摆放策略数据流读完了心里要有三张图第一张是整个block的主数据通路从输入到输出怎么流第二张是每个mem被谁驱动、又去驱动谁第三张是大概的拥塞预期哪些区域肯定要过很多线。转化策略也很直接。首先是“按模块聚类”把同一个逻辑域下用到的mem放在那个逻辑域旁边别跨域乱放。其次是“按端口方向摆放”每个mem的pin方向要尽量朝向它所连接的那一侧逻辑。比如某个mem的数据输入来自上方模块数据输出送到下方模块那它最好放在两者之间pin朝上下方向不要横着摆。最后是“按总线宽度预留通道”有宽总线经过的区域提前留出走线通道别让成排的mem把通道堵死。这一步做完你其实已经具备手工摆放的全部输入了。后面如果你愿意可以完全手摆如果mem太多也可以交给工具自动摆然后人工检查修正。下面就说Innovus里怎么落地这些思路。2. Innovus里常用的mem摆放命令与思路2.1 先定边界和基本约束再谈自动化很多新手一上来就点place_fp_macro结果工具一通乱摆看着还挺整齐实际上完全不管数据流。原因很简单工具默认的优化目标未必是你心里的数据流。我习惯先手动把大框架定死再让工具在框内自由发挥。首先是floorplan边界命令大概是这样的floorPlan -r 0.85 0.7 10 10 10 10这里-r 0.85是长宽比0.7是core利用率后面四个10是core到左右上下边界的间距。利用率这个参数很重要mem多的设计建议先保守一点0.65到0.75都是常见的因为后面还要给mem之间的布线通道留空间。然后是给每个mem或者每组mem加halo和fence。Halo是每个macro四周的禁止区域相当于给mem和标准单元之间留出隔离带Fence是把一组instance限制在一个矩形区域内。这两个约束是工具自动摆放和后续place最重要的“边界”。# 给所有mem加2um halo addHaloToInst -halo {2 2 2 2} [dbGet top.insts.cell.type macro] # 给一组mem创建fence addFence mem_group1 -type hard -area {50 50 300 200}addFence这个命令本质上不是“摆放”而是告诉工具这些mem只能在指定区域里动。对于数据流已经明确的分组我建议直接手动把fence画出来。画fence的时候心里要有数fence不是越大越好太大了mem会在里面乱跑数据流方向又乱了太小了放不下或者导致mem排成很奇怪的形状。一般先把一组mem估计一个大致面积总和再按长宽比画一个约1.2到1.5倍的矩形留一点buffer给自动摆放去调整。2.2 用place_fp_macro跑一波“数据流自动摆放”定完fence和halo就可以上工具自动摆了。Innovus里负责macro摆放的命令主要是place_fp_macro它支持基于数据流的自动摆放不同版本叫法略有差异老EDI里可能有类似选项。place_fp_macro -autoDf -halo {2 2 2 2} -instList [allMacros]-autoDf表示让工具根据网表连接关系来优化macro之间的相对位置。工具会计算各个macro之间的连接权重然后把连接紧密的macro尽量放近。这个思路和手工找“铁哥们”是一样的只是工具算得更细能同时考虑几十上百个macro的相互连接。不过要提醒一点place_fp_macro -autoDf不等于“一劳永逸”。它对纯数字逻辑之间的mem效果不错但对那种“模拟约束”、“特殊时序要求”、“多bit总线对齐”的场景工具不一定知道你的深层需求。举个例子一组总线宽度很大的mem工具可能因为连接权重认为它们中间还需要放若干小cell就把mem拆散了但在物理设计上你更希望它们并排摆成整齐的阵列方便bus routing和shielding。这种时候工具自动结果只能作为起点。如果你不想完全自由自动也可以自己指定初始位置让工具只做局部调整。比如先用setInstancePlacement给关键mem一个初始位置再跑place_fp_macro加-dontRestore之类的保护选项具体看版本让工具保留一部分人工约束。2.3 自动结果只是起点手工精修查这几项自动摆放完成后别急着往下走先做几项检查。第一是查PIN方向。选中一个mem看它的所有pin的平均朝向。如果mem的data pin朝右、但连到它的模块在左边那这个mem基本是要旋转的后面绕线一定多。Innovus里可以通过dbGet快速检查mem的orient和bbox也可以直接在GUI里看pin的飞线聚集方向。第二是查拥挤度和通道。跑一步快速拥塞预估early global route或congestion estimate看mem区域周围有没有大片的红黄色热点。红点通常出现在两个区域之间只有一条细通道的地方需要判断是不是mem把路堵了必要时去掉某些halo或把mem往边上挪一挪。第三是查对齐网格。mem最好对齐到core的row或某条统一网格上这样后面电源地轨、clock mesh都更好处理。如果工具摆出来的位置乱七八糟用snapFpSite之类的命令具体名称看版本或手动微调把它们对齐。我个人的经验是自动摆放 手动精修的时间比例大概在3:7。自动帮你搞定70%的mass placement剩下30%的“数据流关键节点”一定要自己看。比如最大的那几个SRAM、处在关键路径中间的那些register file必须单独确认它们的位置和方向。3. 一次完整实操从数据流分析到mem落位3.1 第一步用飞线和连接密度锁定数据流方向拿一个我最近做的模块举例。顶层有四个子模块input_ctrl、compute_top、output_ctrl以及一个central buffer区。mem分布如下input_ctrl旁边有8颗input SRAMcompute_top里面有40多颗中间结果SRAM和register fileoutput_ctrl旁边有12颗output SRAM。一开始我什么都不做先加载netlist和 floorplan打开GUI的flyline显示。结果很明显input端口在左侧出来的大量总线先连到input SRAM再从SRAM连到compute_topcompute_top的中间结果又连到中间SRAM阵列和output_ctrl最后输出在右侧。主数据流就是“左进右出”中间经过若干存储节点。再用dbGet扫了一下每个mem的top-5连接逻辑块把连接数大于阈值的模块列入“强关联”列表。这一步是为了验证GUI飞线的直觉# 简单统计当前所有macro与模块的连接关系思路 set allMacros [dbGet top.insts.cell.type macro] foreach m $allMacros { set nets [dbGet $m.nets.name] puts $m : [llength $nets] nets }实际用的时候我会写脚本把每个macro的net连接对象分类统计例如“这个SRAM有32根net连接到compute_top有8根连到output_ctrl”这样就能定量地判断一个mem到底该靠近谁。3.2 第二步给mem分组并加fence/halo数据流方向定了以后分组几乎是自然的input SRAM是一组compute_top里的中间SRAM按子模块分成若干组output SRAM是一组。我不会把几十个mem全丢在一个大fence里那样工具很难摆出有序结构。实际操作中我用类似这样的脚本# 按组别依次画fence addFence input_sram_grp -type hard -area {10 100 120 300} addFence comp_sram_grp1 -type hard -area {130 100 300 350} addFence comp_sram_grp2 -type hard -area {310 100 500 350} addFence output_sram_grp -type hard -area {520 100 650 300} # 对关键mem单独加更大的halo addHaloToInst -halo {5 5 5 5} comp_sram_grp1fence画好后我会把每个fence内存的mem数量、mem总面积、预估标准单元面积大概加一下确保fence面积没有太离谱。计算方法很简单把mem的面积从LEF里读出来加和再除以目标利用率。比如一组mem总面积是20000um²如果目标利用率0.7那么fence面积至少给28500um²我一般再乘1.2给到34200um²左右留出走线通道和多余度。fence画大了不要紧那部分空隙可以作为布线缓冲画小了后面肯定要返工。3.3 第三步逐个方向校正pin与orientfence画完我用place_fp_macro先跑一版自动摆放然后开始人工检查。这一步最花时间但也是整个floorplan最值钱的部分。检查方法是把GUI切换到“只显示macro和它们的前几级连接”视图然后逐个看mem的pin分布。比如有一颗input SRAM它的数据输入来自左侧input_ctrl数据输出去右侧compute_top。理想状态下它的pin应该左右分布即library里的pin结构是左右出pin的朝向。如果工具给它摆成上下朝向那肯定不对我直接旋转# 将指定instance旋转为R0方向具体orient依库而定 setInstancePlacement inst_name -orient R0这里要提一个容易被忽视的点同一个mem在不同的fence里最优orient可能不一样。你不能图省事让所有mem都朝一个方向而是要看每个mem连到外部逻辑的方向。批处理可以做但最好按组来比如input组统一朝右、output组统一朝左、中间处理组上下对接这样整体数据流看起来非常清晰后面绕线也直。另外mem的pin如果分布四周那就尽量让每条边的pin都平均对应周围的逻辑不要出现“一堆pin朝着无人的空地”这种尴尬情况。3.4 第四步快速验证与迭代摆完之后不能直接拍板我一般会做三件事快速congestion评估、快速时序估算、检查macro之间的间距。# 检查macro间距是否有violation checkPlacement快速congestion评估我一般只跑early global route不用全量布线。跑完看热力图如果某些通道的congestion超过预设阈值比如大于120%记下来回去调整对应区域的mem位置。这个环节通常会迭代两三轮。一个有用的技巧每次调整完把当前的macro位置导出成脚本存档writeFpMacroPlacement flow_ok.macro_placement后面改乱了随时可以恢复回这个版本省得手滑把好不容易调好的位置丢掉。不同版本的Innovus导出命令可能不同老版本我记得是saveFpMacroPlacement之类用之前可以查一下help write*。三到五轮迭代之后通常会得到一个“看起来舒服、飞线不交叉、拥塞可控”的mem摆放结果。这时候再去做standard cell place你的起点就比之前高了一大截。4. 常见问题与排障记录4.1 局部拥塞爆红怎么快速定位最常遇见的坑是fence画好了mem也放对了但某个小区域依然爆红。这种情况先别急着怀疑mem位置用工具把congestion热点上覆盖的instance列表dump出来看看里面是buffer多还是组合逻辑多。如果是buffer多很可能是你的数据流主通道刚好从这里出去位置本身没错但缺少足够的绕线资源这时候给这个区域多加一条走线通道往往就解决了。如果热点紧贴着mem的边缘那大概率是mem的halo不够或者mem的pin方向导致所有信号都从一个角落挤出去。我的处理办法是在热点侧给mem额外加一层halo或者把mem旋转180度让pin分布更均匀。还有一种隐蔽原因多个mem的clock pin都被摆到相邻位置导致CTS buffer在那一小片扎堆。这种看一眼clock pin分布就能发现需要做的是调整mem的orient让clock pin朝不同方向分散开或者把同一个时钟域的mem在fence内做小幅位移避免clock pin叠在一起。4.2 mem之间走线通道不够绕得飞起有时候mem之间的间距确实留了但布线时发现中间有大量宽总线要通过通道还是不够。这里要分清“通道面积”和“通道可达性”的区别。面积够但如果通道两侧被其他mem的halo挡住入口很窄总线进不来一样会绕。检查方法就是在GUI里沿着通道走一遍看有没有“瓶口”。解决的办法很直接把通道两侧的mem向外挪一点保证通道宽度在整个路径上是均匀的。不要只在中间加宽两边入口窄等于白加。还有通道内部尽量不要放标准单元因为布线阶段这些位置需要给via留空间。可以用placement blockage挡住createPlacementBlockage -area {x1 y1 x2 y2} -type hard4.3 自动摆的mem太散CTS做不干净place_fp_macro -autoDf自动出来的结果有时候会把同一组mem拆得很散表面上没有congestion问题但CTS阶段很痛苦。因为同一个时钟域的mem分散后clock tree的depth和skew非常难控制。我后来习惯在自动摆放之后用脚本检查每个fence内的mem是否都还在原fence里如果工具把某些mem扔到别处去了我会重新手动把它们放回所属fence甚至可以加一个更小的硬fence限制它们。CTS敏感的mem组比如register file、大容量的SRAM array我甚至不建议用-autoDf全自动直接手工把它们的相对位置固定好再让工具只摆无关紧要的小macro。4.4 顺手收藏几条高频命令速查这里把上面提到的常用命令汇总一下方便直接抄操作目的参考命令设置floorplan边界floorPlan -r ratio util left bottom right top给mem加haloaddHaloToInst -halo {d d d d} inst_list创建fenceaddFence name -type hard -area {x1 y1 x2 y2}自动摆放macroplace_fp_macro -autoDf手动设置instance位置朝向setInstancePlacement inst -orient R0检查placement合法性checkPlacement快速拥塞评估reportCongestion -hotspot按版本确认导出当前macro摆放writeFpMacroPlacement file再补充一个经常被问到的技巧怎么在Innovus里快速选中某个名字带特定特征的标准单元或pg term。比如有人想选中名叫biasnw的PG term可以这样# 先找到名为biasnw的instance的pgTerms dbGet [dbGet top.insts.name biasnw].pgTerms.name # 如果要模糊匹配所有类似名字的instance dbGet [dbGet top.insts.name *biasnw*].pgTerms.name这条命令在排查PG连接、分析IR drop或者做特殊net查证时非常实用也算是一个额外小抄。5. 一些值得养成的实操习惯5.1 把摆放模板脚本化同一个项目迭代版本非常多每次重新读入netlist之后如果把几十个fence、halo、orient重新手画一遍既慢又容易出错。我通常会把floorplan阶段的设置全部整理成一个Tcl脚本里面按组注释好每个区域是干什么的、为什么这么画每次新版本直接source一遍再根据具体情况微调。这样做还有个好处换人接手时光看脚本就能快速理解整个floorplan的思路。脚本里我会把每个fence的逻辑作用写清楚比如# input SRAM buffer region, data flow left-right。等过几个月再回来看也能很快回忆起当初为什么这么摆。5.2 不同时钟域或宽总线单独划区如果设计里有多个时钟域mem摆放时尽量按时钟域划分物理区域。这个习惯能帮你省掉大量CTS阶段的时间。不同时钟域之间的mem混放表面上可能没有违例但跨时钟域的线通常更长约束也更多放一起会互相干扰。宽总线也是个敏感点。比如128bit或256bit的bus如果可能把它们的源端、目的端、中间mem摆成一条直线别让总线里面弯来弯去。这个“直线原则”在布局初期多花五分钟后面布线阶段能省一天。5.3 早期评估别等时序乱成一锅粥再调有些朋友喜欢所有东西都放完、跑完CTS再回头看问题那时候改floorplan成本极高。我的建议是每调整一轮mem摆放都做一次快速时序评估哪怕没有完整约束只做estimate看看关键路径的趋势。如果某些路径因为摆放变长了立刻微调别拖到后期。早期评估不要求绝对准确只要趋势一致就行。比如某条关键路径从500ps变成700ps说明这轮摆放可能把数据流带偏了回去看飞线十有八九是某个mem的位置不对。这种“小步快跑”的验证节奏比一次性把所有东西放到位再检查要高效得多。最后说说我自己的体会mem多不是问题问题是你能不能站在数据流的角度看floorplan。每次摆mem之前我都会问自己一句如果我是这捆数据线从这里走到那里我希望路上有没有墙答案越清晰floorplan就越顺。希望这篇笔记也能帮你找到那种“一眼看去就知道数据在怎么流动”的摆放状态。
返回列表