ARTICLE DETAIL

资讯详情

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

数字后端必读:Memory布局如何以数据流为导向?Innovus摆Mem实战

数字后端必读:Memory布局如何以数据流为导向?Innovus摆Mem实战 1. 摆mem靠蛮力必翻车数据流才是floorplan的出发点做了几年数字后端我越来越确信一件事Memory类macro的摆放从来不是一个“排整齐”的问题而是一个“符合数据流”的问题。尤其是当一颗芯片里出现几十颗MEM、形态还都是几十微米宽的硬macro时表面上要解决的是“怎么塞得下”实际上要解决的是“数据从哪边进来、从哪边出去、中间要经过谁”。innovus里摆mem的理由可以有无数种但最优先的那条理由永远是数据流方向。很多刚做后端的同学拿到floorplan任务第一反应就是打开instance列表把MEM按面积从小到大排一下然后尽量让它们共用一条电源rail、尽量让macro边缘对齐。这种做法的确能让版图看起来很整洁但等place之后的时序报告出来一条从MEM输出到MEM输入的路径横穿半个die你就知道什么叫“摆反了”。物理上的绕线本质上是前端RTL中数据流动顺序的投影。RTL里是a模块算完给b模块b模块缓存完再给c模块那物理上就应该顺着这个方向摆。一旦中间某个缓存macro被随手放到数据流的侧后方后端的绕线资源、时序裕量、甚至时钟树长度都要为这个错误买单。那什么是“数据流”说人话就是数据从系统入口进来之后经过哪些模块、以什么顺序、往哪个方向流动。拿一颗图像处理芯片举例可能是DDR接口把数据读进总线矩阵总线分发给预处理模块预处理结果写到ping-pang buffer接下来算法核读这个buffer算完再写到输出Buffer最后从输出接口出去。这里的方向非常清楚总线矩阵在左预处理和算法核在中间输出在右。那么相关MEM就应该是“输入侧缓存靠入口中间结果缓存靠处理核输出缓存靠出口”。所以真正合理的流程不是先看每个MEM多大、能不能对齐而是先花半小时把schematic或者RTL的模块关系梳理一遍弄清楚每个MEM在数据通路上处在哪个阶段。这里我的习惯是打开innovus之后先不急着place任何东西而是先把MEM清单过一遍确认每个实例挂在哪个hierarchical module下面、管脚的名字大概对应什么功能。一般来说后端netlist里的instance命名都会保留前端模块名的特征比如u_isp_ibuf_0、u_scale_obuf_2从名字就能猜到它属于哪个功能块。1.1 为什么MEM特别吃数据流这一套标准单元可以随便挪坏了重新place一下就行大不了让优化器多跑几轮。但MEM不一样它面积大、走线层固定、pin通常只在某几条边上搬一次的成本非常高。而且memory两端必定连接大量数据总线和控制逻辑这些逻辑往往是高扇出、宽总线一旦macro位置与数据流方向相悖绕线代价立刻反映在congestion和时序上。另外MEM的pin通常很集中端口的访问方向是确定的。比如一个SRAM的DIN和ADDR在一侧DOUT在另一侧这本身就自带一个方向。如果摆放时强行让DOUT指向数据流的上一级那从DOUT引出来的数据线就要绕到macro背后完全违背了“让线直着走”的原则。影响的不只是一根net而是一整组总线。数据总线通常都是32位、64位甚至更宽一整组线绕远路再密的绕线资源也不够折腾。1.2 动手前先画一张“粗粒度流向图”这一步不一定用EDA工具拿张纸画就够。把顶层模块拆成几个大块输入接口、处理模块、缓存、输出接口然后标出每个MEM分别属于哪个块数据在块与块之间的走向是什么。我在实际项目里通常这样梳理先看top level的port方向确定芯片级数据入口和出口在哪条边再打开hierarchy tree把所有包含MEM的submodule列出来根据RTL的数据通路连接关系标注出“谁先谁后”最后在版图范围里画一条或几条主数据流方向比如“自左向右”“自下向上包围”。这张图不用精确只要方向对就行。后续所有MEM摆放的决策都以这张图为最高优先级。它比任何工具自动生成的floorplan都可靠因为工具毕竟看不到系统级的数据流。2. 在Innovus里把mem“按流向”摆出来从全盘规划到手动微调的实操链路梳理清楚数据流之后才轮到innovus上场。这里我一般把操作分成三步盘点、分区、落位。很多资料只讲落位不讲前两步结果就是照着命令敲完还是不知道坐标怎么定。2.1 先把MEM清单和层次归属盘清楚第一步是拿到设计里所有MEM的准确清单并且按层次归类。Innovus的Tcl命令行里可以直接过滤# 列出所有名字里带mem/SRAM/RAM的实例 get_insts -filter ref_name ~ *MEM* get_insts -filter ref_name ~ *SRAM* get_insts -filter ref_name ~ *RAM*实际项目里MEM命名不一定统一有的叫u_xxx_mem_0有的叫spram_32x64_w0所以我一般直接按ref_name过滤master cell而不是只按instance名。拿到清单后再查看每个实例所在的hierarchy用类似dbGet的方式把inst归属串出来dbGet top.insts.name -p然后按module维度做一次汇总哪个module下面有几颗MEM它们大致是哪种类型单端口、双端口、同步、异步数据位宽是多少。这个汇总表就是后面分组的依据。不要把这一步省掉因为后续在进行plan group或者region约束时必须知道哪些MEM是“一伙的”。2.2 粗粒度落位先分区后精调我见过不少人一上来就placeInstance给每个MEM填一个精确坐标。其实这个动作应该放到最后前面更重要的是“分区”。所谓分区就是把数据流上相近的MEM放进同一个区域先把它们作为一个整体放在版图的某个方位然后再在区域内部调整相对位置。在innovus里可以用add_plan_group来建组比如把接收路径上的几颗缓存MEM放在一组add_plan_group -name grp_rx_ibuf \ -inst {u_rx_mem0 u_rx_mem1 u_rx_mem2 u_rx_mem3} \ -region {100 200 400 400}建完组之后先移动整个组而不是逐个MEM去挪。组的位置大体符合数据流方向后再拆开精调。如果设计里MEM实在太多也可以考虑直接用partition或者region把功能块圈起来让工具在区域内做进一步摆放。这里有个很实用的经验先粗后细全程大局观。粗粒度阶段如果发现某个组的落点和数据流方向不匹配后面精调还能救得回来但如果一开始就直接逐颗摆等全部摆完发现整体方向错了那个返工量足够让人怀疑人生。2.3 Innovus里几个常用的落地操作实际落位时我常用的是这几个操作placeInstance直接指定坐标和方向适合手动精调单颗macroGUI Floorplan Editor可视化拖拽适合在大局阶段快速试位置Pin/pad placement信息如果MEM大量连接到某个接口或某个IP优先把连接密集的方向对准那个模块Flyline显示在GUI里选中某条关键数据总线观察flyline的走向来判断当前一组MEM的方向对不对。落位时还要注意“让总线尽量直着走”具体做法是观察一组总线两端的模块位置把MEM端口朝向那一边。比如一颗SRAM的DOUT要送给下游的运算模块那么DOUT所在的边就应当面向运算模块所在的方向不要因为旁边有一颗同构SRAM为了对齐而强行翻转。3. 一堆mem怎么组阵列对齐、翻转、bit方向和供电走廊一起考虑当MEM数量一多单颗Mem摆清楚了还不够阵列形式也直接影响数据流。尤其是同构MEM很多的设计例如Ping-pang buffer、Way cache、多通道行缓存摆成阵列之后能不能跟数据流顺上是另一个层次的挑战。3.1 多颗同构MEM的阵列排布同构MEM最容易摆成整齐的2x2、1x4或者4x4阵列这个方向上大家都会。但关键在于阵列的“主轴”方向要跟数据流主轴一致。比如四颗Ping-pang buffer数据流是从总线进、从算法核出那么这四颗buffer应该沿着数据流动方向排列成一行而不是两行两列并排这样才能让前级总线均匀地接到每颗buffer的输入端后级算法核也均匀地接走输出端。另外阵列中每颗mem的地址线和数据线通常要共享一部分逻辑。如果阵列方向和数据流垂直共享逻辑的连线就会两侧拉扯导致拥塞集中在阵列中间。我的经验是先看连接关系最密集的net方向再决定阵列是横排还是纵排。如果每颗MEM的端口都在同一侧那阵列方向最好让这些端口排成一条直线方便后续的数据总线直着打过去。3.2 方向不是随便转pin朝向要跟着数据流走方向选择上很多人追求把同一组MEM做成统一方向看起来整齐。但更重要的判断依据是pin朝向。通常一颗SRAM宏的端口不会四边都有一般集中在其中两边一边是输入和地址另一边是输出。摆放时需要考虑的是哪个模块主要驱动它的输入哪个模块主要接收它的输出然后优先把高频、宽带的数据总线方向摆直。举个例子假设一颗MEM输入来自左边的预处理模块输出要送到右边的算法核那么最理想的方向就是R0且端口正好左右对齐。如果因为某个power strap或alignment的原因非要转成R180那输入输出端口也反了所有总线都要绕到背面去接这就得不偿失。遇到这种取舍我通常优先保证主数据流方向再考虑功率/热问题因为数据流方向的错误后续几乎无法靠绕线救回来。3.3 留出走线通道和供电通道MEM摆得再顺手也得给走线留活路。一组MEM之间通常要走数据总线、控制信号和时钟旁边还要走电源strap。如果两颗MEM之间贴得太近只有macro自带的空间那这个通道最后一定变成congestion热点。实际设计中我一般会在阵列内部和阵列边缘留出额外的通道宽度具体留多少要看穿过的总线数量和布线层资源。供电通道这里单独提一下。MEM功耗都不小阵列供电需要足够的strap宽度。如果为了“紧凑”把MEM排得太挤后面加strap时发现没有空间就得回头重新调位置那种改动比一开始多留几微米要痛苦得多。所以摆MEM的早期就要考虑好VDD/VSS strap的走向给供电网络预留空间。4. 摆完不等于完事用congestion、时序和pin access来验证数据流逻辑摆完MEM第一步不是庆祝而是先跑一轮快速验证看数据流方向是不是真的摆对了。这里我通常看三样东西congestion、时序、pin access。它们能分别暴露不同层面的问题。4.1 用congestion map反向检验数据流congestion map是验证MEM摆放是否符合数据流最直观的手段。在innovus里跑完initial placement或者global routing的早期版本后打开congestion map如果某些区域出现大片红黄颜色那基本可以断言附近的数据流方向出了问题。我见过一个典型案例一组MEM从输入到输出的数据流其实是自左向右但摆放时为了迁就某个IP的位置把整组MEM竖着排成了两列。结果就是总线从左边进来绕到上边再绕回右边相当于多走了一个“U”字。congestion map上这个U字路径整条都是红的。后来我把这两列改成顺着数据流方向的一列排开红区立刻消掉大半而且关键路径的延时也降下来了。congestion地图不只是看密度更是检验“方向”的工具。4.2 对时序报告里的关键路径做“路径投影”数据流摆对了大部分关键路径应该“近”而不是“远”。所以在place之后我会挑WPW里最差的几条路径出来看它们的起点和终点都落在版图哪个位置。如果关键路径起点是一颗MEM的输出终点是另一颗MEM的输入但两颗MEM之间的物理距离却占了半个芯片宽度那路径再怎么优化也难救。这个判断方法叫“路径投影”把时序路径的关键跨度对应到版图上然后反推是不是中间某个MEM插错了位置导致路径被迫绕远。有时候关键路径其实很短但某颗MEM端口方向反向导致pin到逻辑门本身就要多绕一段这也算数据流问题。4.3 pin access和SI也不可放过数据流方向摆对了但个别MEM的pin access可能仍然很差。比如MEM的输出pin刚好被旁边另一颗MEM挡在内部数据线要绕到阵列外侧才能接出来。这时就得微调相邻MEM的相对位置或者方向保证所有pin都能顺利引出。还有一个容易被忽略的DP问题就是信号完整性。长距离并行总线在MEM密集区域容易产生耦合串扰所以验证阶段要顺便看一眼SI风险较大的net是否横穿了MEM阵列。若是横穿就说明要么数据流走向有问题要么阵列区域缺少屏蔽/隔离空间这种问题拖到signoff阶段再处理就很被动了。5. 那些年我碰过的mem布局翻车现场和排查经验我踩过的MEM摆放坑比看过的教程多得多这里专门挑几个典型“翻车现场”出来讲每个背后的排查链路都比结论更有价值。5.1 摆反了行缓存整条图像通路都在绕圈某次做图像传感器接口设计前端有若干行缓存MEMRTL上的数据流是从mipi接收进来经过行缓存写到后续的HDR处理模块。我一开始按面积摆放把行缓存放在了整个block的角落想着那里空着结果后续HDR模块的数据总线要从math corner绕大半圈才能接到行缓存输出。时序收敛不了congestion也炸。排查时我先看flyline选中行缓存输出到HDR模块的数据总线发现所有总线都在版图上画了一道巨大的弧线。这时候才意识到问题出在行缓存的绝对位置偏离了数据流主方向。解决方式是先把行缓存组搬到接收接口和处理模块之间的中轴线上方向让DOUT对着处理模块整个问题立刻缓解。从那以后我就养成了习惯每摆一组MEM之前先在高亮显示的情况下选中它的关键数据总线看看flyline指向哪个方向。5.2 阵列排得太整齐结果BIST控制器绕了半圈另一个印象深刻的案例是MEM阵列本身排得非常漂亮整齐的1x8方向和间距都完美。结果后面发现BIST控制器的位置在阵列的另一端BIST跟每颗MEM之间的控制信号形成了一组极长的平行走线不仅绕远还造成时钟信号在阵列内部来回走skew很难做小。排查的时候我看了BIST到各MEM的连接分布才发现问题不在于MEM阵列摆放而在于BIST这个“公共控制逻辑”没有跟着阵列主轴走。解决方法是把BIST控制器挪到阵列的中间位置或者重新调整阵列的起始方向让BIST可以从某一侧平均访问到所有MEM而不是从一端到另一端拉一条长线。这个案例也提醒我MEM摆放不只是看MEM本身还要把与它们强耦合的场景都放在一个层面上考虑。5.3 只顾方向整齐结果端口离逻辑远了还有一次为了追求一组MEM方向完全一致我把其中两三颗MEM的方向都统一成R0。但后来发现其中一颗MEM的DOUT端口实际上应该朝下对接下方模块经R0后端口朝上导致数据总线要从上方绕到下边整整绕了四排单元。当时排查时在GUI里看到那组总线明显“拽”着绕了一个角才意识到方向整齐不等于方向合理。物理上每一颗MEM的端口位置都是独立的数据流决策。同构阵列里同一排的MEM方向一致是合理目标但如果有某一颗的输出对象和别的MEM明显不同就应当单独处理不能为了美观去牺牲数据流的径直性。6. 如果让我重摆一次对正在调floorplan的你说几句实在话所有这些经验沉淀下来其实可以浓缩成几条非常朴素的规矩。这些规矩不高端但每次照着做都能少走很多弯路。6.1 顺序问题先数据流再阵列最后调细节摆MEM完整顺序应该是先定数据流主轴再摆功能组然后组内排阵列最后才微调方向和坐标。很多人喜欢先建阵列、再考虑数据流这个顺序一旦反了后面所有操作都在给第一次错误决定买单。数据流主轴定下来之后哪怕阵列稍微不整齐也比“整整齐齐但方向全错”强得多。6.2 不要迷信工具自动摆innovus确实有Plan Design、自动Floorplan之类的功能也能给出看起来不错的布局但它看不到你系统级的“数据往哪个方向流动”。尤其当MEM多到几十颗、接口分布在芯片不同侧时工具给出的布局往往是基于连线长度和拥塞的数学优化而不是数据流的逻辑顺序。我个人习惯是把工具自动布局当成“第二意见”而不是最终结果。先用工具跑一版看它的结果跟我的数据流主方向是否一致。一致的可以参考不一致的基本可以判定工具没有感知到系统级方向。手工摆虽然慢但方向对了后面整个流程都会顺。6.3 “看起来像绕线挤”的问题很可能是MEM位置错最后提醒一点很多后端工程师遇到congestion第一反应是加routing margin、调spare cell、改layer direction很少先怀疑MEM布局。但根据我的经验在MEM密集的设计里本地拥塞问题的根源一大半是MEM相对数据流的位置和方向不对。与其在congestion map上不停加约束不如先退回floorplan阶段检查一下附近几颗MEM的摆放是否符合数据流方向端口是否朝向了真正接收它的模块。这一个动作往往比任何绕线优化都更直接。我把这个方法称为“MEM布局的事前验证”其实原理很简单数据流方向在RTL里已经是定死的物理约束后端做的只是把它还原到版图上。还原得越精确后面的日子越好过。希望这篇笔记能帮你少踩几个我当年踩过的坑。
返回列表