ARTICLE DETAIL

资讯详情

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

SMIC Memory时序库从lib转db全流程实战:配置、脚本与踩坑指南

SMIC Memory时序库从lib转db全流程实战:配置、脚本与踩坑指南 前阵子帮一个项目组整理SMIC工艺下的Memory时序库核心工作就是把Memory Compiler吐出来的一堆.lib文件用Library Compiler批量换成综合和后端能直接吃进去的.db。整个过程听起来很简单实际走下来有不少细节稍不注意就是面积虚大、timing对不上、功耗表缺失这类问题。今天把这次实战从头到尾拆开讲清楚从SMIC Memory配置到lc_shell脚本编写再到坑点排查一次性讲透。做数字IC的兄弟都清楚Memory在芯片里的地位很特殊。它通常占掉芯片面积的大头又是时序和功耗分析的难点。Synopsys的Memory Compiler会生成.libLiberty时序库这个文件是文本格式人眼能看但综合工具、STA工具不会直接拿它去跑。需要用Library Compilerlc_shell把它转成二进制格式的.db文件。这一步看起来只是格式转换实际牵扯到库名、时序模型、功耗模型、面积、电压单位等一系列信息的解析和重构出一点差错后面DC综合和PT签核就会埋雷。这篇文章适合正在做数字后端、前端集成或者PDK/库维护的工程师。如果你是刚接触Memory Compiler的应届生或者从其他工艺转SMIC的老手都能在里边找到可以直接抄作业的脚本和排查思路。1. 项目概述为什么要碰Memory的lib转db1.1 Memory在数字流程中的特殊地位几乎所有SoC里都有SRAM、ROM这类存储器寄存器和触发器堆出来的memory在面积和功耗上没有竞争力。实际项目里大块缓存、FIFO、寄存器堆都是直接调用Memory Compiler生成的硬核macro。这些macro不是像标准单元那样的简单逻辑它们内部有复杂的位线、字线、敏感放大器、时序控制电路Cell library里对应一个完整的时序模型。在做逻辑综合时Design Compiler需要知道每个memory单元的面积、输入pin的cap、时序弧setup/hold/output delay、功耗特性。这些信息在Memory Compiler生成的.lib里都有。但DC默认加载.db格式Synopsys工具链用二进制db去加速读取和优化。直接用.lib也能跑DC吗早期版本某些流程能读但正规流程都是先转db一是有性能优势二是方便库里统一管理三是db在版本兼容和加密上更省心。SMIC的Memory Compiler通常一次会生成多个corner的lib比如typical、slow、fast还可能分为ss/tt/ff更细分的PVT组合。这些lib文件需要统一转成对应的db才能分别用于setup和hold分析。转db不是简单改个后缀里面有很多工程层面的决策点。1.2 lib和db到底差在哪.lib是Liberty格式的文本文件遵循IEEE 1481标准里面描述了cell的name、pins、direction、capacitance、timing arcs、power模型。可以用文本编辑器打开也可以直接用脚本解析。db是Synopsys的二进制格式本质上是lib的编译后产物解析速度更快也支持base library之间的link操作。从内容上说db并没有增减lib的信息它是一一对应的。Library Compiler在转库时也会做一致性检查如果lib有语法错误、定义缺失、单位不明转换就会报错或warning。所以很多人问“能不能直接用lib替代db”技术上部分工具支持但芯片流程里普遍的做法是db优先。特别是在multi-corner multi-mode流程里db的加载效率比lib高很多综合几千个标准单元加上几十个memory的库文本解析的差距会非常明显。1.3 这次实战我打算怎么讲整条链路可以分成两段前一段是SMIC Memory配置决定生成什么样的.lib后一段是Library Compiler转换决定.lib如何变成.db。很多人在转库时报错回头查了半天根因其实在Memory Compiler配置那一步比如输出寄存器选项没选对或者功耗模型没打开生成的lib本身就不完整。所以这篇文章不会只讲lc_shell那几行命令而是把Memory配置和转库结合起来讲让整个流程能高效闭环。2. SMIC Memory配置环节先把源文件做对2.1 Memory Compiler选型与工艺映射SMIC的Memory IP一般通过工艺厂或第三方IP公司提供的Compiler工具生成比如自研的SRAM Compiler或者用的Sage Micro/力旺等第三方编译器。选型时第一个要确认的是工艺节点和金属层。SMIC工艺有55nm、40nm、28nm、14nm等不同手机/wire bonded封装还影响金属层选择这些会直接决定memory macro的面积和速度。Memory Compiler工具里一般会先选Library/Technology然后进入SRAM/ROM类型选择。这一步务必和Foundry提供的PDK版本对齐。PDK版本对不齐生成的lib在时序上和signoff标准可能有差异。我见过有人拿着新工艺的Compiler去生成旧PDK的库结果db里的时序模型和后端signoff时拿到的SPICE仿真结果差得离谱。配置界面里通常要填这些基本参数Memory类型Single Port、Dual Port、Two Port、宽度Word Width、深度Number of Words、Column Mux Ratio、Output Register、功耗优化模式。每个选项背后都有一整套电路架构选择。2.2 关键配置项逐条拆解先说宽度和深度。Word Width和Number of Words是Memory的基本规格一般直接来自芯片架构需求。要注意的是深度变化会显著影响macro面积和时序同样的16Kx32和32Kx16面积不是简单线性关系而是取决于编译器内部的bank划分和bitcell排布。我们在实际项目里会让架构师先给需求再让Memory Compiler跑一遍看面积和时序报告是否满足约束不满足就调整宽度、深度或mux ratio。Column Mux Ratio也叫列复用比常见的有4、8、16。它的含义是同一根IO pin被多少列的bitcell共用。mux越大单根IO对应的bitcell列数越多位线负载越大访问速度会变慢但面积会更小因为外围电路sense amplifier、写驱动可以减少。反过来mux越小速度更快面积更大。选mux时要结合工作频率和预算面积通常先默认4或8然后根据时序余量微调。Output Register选项决定了memory是否带流水线寄存器输出。带output register的memory有更好的时序性能能显著降低输出路径上的delay但会增加一拍延迟同时面积和功耗也会略高。很多前端工程师在RTL里已经有了reg输出再用Compiler的output register就会造成两级寄存器白白增加延迟和功耗。这里要提前和前端确认清楚否则生成出来的lib在综合时时序会偏紧。功耗优化模式一般有High Speed、Balance、Low Power几种。SMIC工艺下Low Power模式通常通过降低位线摆幅、关断不需要的bank、调整cell电压等方式实现代价是访问变慢。如果芯片是低功耗SoCMemory数量多这个选项值得仔细权衡。High Speed模式适合对频率敏感的关键buffer但要做好功耗超标的风险评估。2.3 配置时的面积、速度、功耗取舍Memory配置不是简单地把参数填完就完事本质上是一个多目标优化问题。面积和速度是直接矛盾的。速度越高往往需要更大的bitcell更强的sense amplifier更精细的时序控制电路这些都会推高面积。功耗和速度也有冲突尤其是在近阈值电压或低电压场景下功耗优化往往会导致cell电流能力下降时序变差。我的习惯是先在典型corner下做一个sweep把深度、宽度固定改变mux ratio、output register和功耗模式生成几组lib然后用Memory Compiler自带的报告对比面积、功耗和access time。选出最优组合后再针对这个组合去生成全corner的lib。这个流程会花点时间但比盲选再返工高效得多。2.4 生成文件怎么检查Memory Compiler生成完成后除了.lib以外通常还会生成.vVerilog model、.gds、.lef、.milkfile或.etm文件。对数字流程来说最关心的是.lib和.v。在进入lc_shell之前至少要做三件事用文本编辑器或grep打开.lib确认library name一般是类似sram_16k*32_tt1p2v_25c.lib的命名这里边包含了corner信息。检查文件头部有没有声明电压、温度、单位。常见错误是单位缺失比如capacitive_load_unit没写转换后电容值异常。检查是否包含时序响应尤其是有没有输出信号transition的查找表。有些简化lib只给了scalar值虽然lc_shell能转但到DC/PT里做尺寸优化时会因为缺少lookup table而非常粗糙。用Memory Compiler生成的.v文件和lib做一次port对应检查。比如256x32的SRAM端口应该有CLK、CEN、WEN、A[7:0]、D[31:0]、Q[31:0]。如果lib里pin定义少了后端的connectivity就会出问题。还有一个小技巧我习惯先用文本工具搜索关键字段比如pin, timing, power, index_1确认lib不是空壳。曾经碰到过编译器生成一个warning部分corner的lib只有一个空模板没有任何timing信息问题就是memory instance数量超过了工具试用limit生成被截断。这种问题不提前发现后面lc_shell照样能转出db但dc使用时会直接报timing library没有arc。3. Library Compiler转换从lib到db的完整实操3.1 工具环境和准备Library Compiler是Synopsys的工具一般在Linux服务器上以lc_shell方式运行。确认环境变量确保lc_shell已经被添加到PATH。可以用which lc_shell验证也可以直接用完整路径调用。转库前先想清楚输出目录结构建议按工艺/corner/类型分目录。比如smic55/sram/tt_1p2v_25c/和smic55/sram/ss_0p99v_125c/分开避免db文件相互污染。数据库统一以后DC的link_library、target_library指向会更干净。在lc_shell脚本里通常需要设置一些变量比如search_path指定lib所在路径。target_library也就是最终生成的db要放到哪里。SHOW_LIBRARY_INFO可用来快速查看库信息。但lc_shell本身不需要复杂的setup它和DC不同不需要启动license那么久但同样需要有效的Synopsys license feature。启动后可以直接交互输入命令也可以执行脚本文件。3.2 一条命令的转换脚本最简单的方式如下set search_path [list . /path/to/inputs] read_lib sram_16k32_tt_1p2v_25c.lib set output_lib [get_object_name [current_design]] write_lib $output_lib -format db -output sram_16k32_tt_1p2v_25c.db在lc_shell里read_lib读入lib后当前design就是那个library库名通常和lib文件里的library(name)一致。可以用current_design查看也可以用get_libs列出所有已加载的library。write_lib命令的完整格式是write_lib sram_16k32_tt_1p2v_25c -format db -output /path/to/output/sram_16k32_tt_1p2v_25c.db这条命令会直接生成db文件。如果是第一次做建议加上report_lib命令先把库信息打印出来确认再写文件。比如report_lib sram_16k32_tt_1p2v_25creport_lib能看到library name、unit、创建时间、cell数量、pin数量等。如果cell数为0基本就是没读进来或者lib有问题。3.3 批量转换脚本Memory一般不是一个两个少则几十多则上百个。手工一条条敲命令显然不行。我习惯写一个Tcl脚本用foreach循环处理一个列表。假设所有lib文件在libs/目录下输出到dbs/目录脚本可以这样写set lib_list [list \ sram_16k32_tt_1p2v_25c \ sram_16k32_ss_0p99v_125c \ sram_16k32_ff_1p32v_0c \ rom_64k16_tt_1p2v_25c \ ] set out_dir /path/to/dbs set lib_dir /path/to/libs foreach lib_name $lib_list { set lib_file ${lib_dir}/${lib_name}.lib if {[file exists $lib_file]} { read_lib $lib_file write_lib $lib_name -format db -output ${out_dir}/${lib_name}.db puts Converted ${lib_name} } else { puts Warning: ${lib_file} not found } }如果corner多、memory多的场景还可以用shell脚本自动扫描目录下所有.lib生成对应.db比如for lib in /path/to/libs/*.lib; do base$(basename $lib .lib) echo read_lib $lib echo write_lib $base -format db -output /path/to/dbs/${base}.db done convert.tcl然后在lc_shell里执行source convert.tcl这样一旦有新增memory只要把新.lib丢进目录重新生成脚本再跑一遍就行。实测下来几十个库的转换也就几分钟瓶颈主要在license和服务器的IO上。3.4 转换后的检查项db生成后不要急着入库先做一轮检查。最直接的是用lc_shell再读一次db或者用report_lib查看。但要特别注意db读取后报告的信息如果和lib一致才能算转换成功。我通常会检查以下几点library name是否和期望一致。cell数量是否完整不要漏cell。每个memory cell的时序arc是否存在查看某个cell下是否有rise/fall transition和setup/hold用report_delay_calc? 更简单的做法是用grep在lib里对比或者db后直接ltrace? lc_shell没有直接report_timing但可以通过report_lib -cell xxx看到pin和arc信息。在lc_shell里如果只是快速确认arc可以report_lib -cell sram_16k32_tt_1p2v_25c会列出每个cell的pins如果只列了pin名没有timing信息那这个lib可能是功能模型不是时序模型。Memory lib的timing信息通常在cell下面以timing()内包含相关_pin和related_pin的方式存在。另外检查power模型用report_lib是不是能看到power_consumption或internal_power信息。对于Memory功耗模型如果缺失跑IR drop或功耗分析就会缺数据。很多Memory Compiler默认生成的lib里internal_power可能只有理论值必须在功耗计算时配合activity factors才能准确估算动态功耗。最后把db放到DC里做一次link和elaborate测试直接用read_db或link_design加载它看是否报错。这一步是终极验证比任何检查都直接。4. 实战中的坑和排查方法4.1 read_lib阶段最常见的报错read_lib报错有很多种我挑几个高频的讲。第一种是“Cannot find library file”或者路径问题。这通常不是lib不存在而是search_path没设置好或者文件名里带了特殊字符。建议在脚本开头用绝对路径避免相对路径带来的麻烦。Memory Compiler生成的文件名有时候带号或者空格最好先用shell重命名成简洁的名字。第二种是“Library name mismatch”或者“Cell count 0”。这往往是lib文件里的library(name)和文件名不一致。Library Compiler内部以lib里的name为主但write_lib时如果名字写错就会写不出来。解决办法是先read_lib再用get_object_name [current_design]确认实际的library name不要凭文件名猜。第三种是“Undefined attribute”或者“Unsupported group”。这常见于lib版本格式太新或太旧Library Compiler版本不兼容。比如新版Liberty里有的一些新属性旧版lc_shell不认识但也不会报error只是忽略结果库信息不完整。遇到这种情况要升级Library Compiler版本或者回到Memory Compiler里的lib生成选项选择兼容的Liberty格式。还有一类是在Windows环境跑lc_shell路径分隔符混乱。Library Compiler主要在Linux上跑Windows上不是不能跑但路径和license的问题很多。我的建议是别折腾放到Linux机器上转。4.2 转换结果和预期不符的问题有时候转出来的db能用但面积不对、timing曲线很怪多半问题出在lib的默认值。比如单位定义。LC转db时如果不明确单位会采用lib里设定的单位。如果lib里电阻单位写成kohm电容单位是pFdb解析出来的load和transition可能和期望差几个数量级。这类问题没法在lc_shell直接看出来要在DC中检查cell面积或pin cap。0.1pf的库被当成1pf综合时buffer sizing就会完全失真。所以转库后我习惯在DC里用report_cell或者report_units确认一下单位和数值范围。尤其是Memory这类大macro面积动辄几万平方微米甚至更大如果db里面积是整数数量级的错误很容易发现如果只是偏5%就要怀疑是不是单位或scaling factor的问题。另一个常见问题是功耗数据异常。Memory的功耗和activity强相关lib里通常给出的是内部功耗查找表包含能量值。如果lib模板没有internal_power而只有leakage_power那转换后功耗报告会缺失dynamic power。这种情况最好回Memory Compiler里检查有没有开启功耗库生成选项。还有一些情况是多个corner的lib相互覆盖。比如tt corner和ss corner的library name都是sram_16k32write_lib到同一个目录时后写的覆盖前面的。这个坑我踩过项目后期发现setup分析用的ss库其实是tt的数据。所以批量转换脚本里一定要用不同或有明确后缀的库名输出文件也严格区分corner目录。4.3 版本兼容和跨工艺注意事项Library Compiler版本和Memory Compiler版本最好保持在同一个Synopsys工具版本周期内。跨大版本比如2019到2022读lib大多数情况没问题但偶尔会有attribute变化导致warning。warning太多时别忽略尤其是“Ignoring attribute xxx”这种说明lib携带的信息可能被丢弃。另外要注意跨工艺复用库的问题。SMIC 55nm的memory db千万不要直接用在40nm的flow里哪怕二者pin兼容时序和功耗物理上完全不一致。有次我见有人为了省转库时间把tt的db复制成ss的db临时跑通仿真后来被PT报了一堆violation反而花更多时间排查。每个corner的db必须独立生成。还有Memory Compiler的lib里经常包含带version信息的header比如“liberty_version 2.0”或“2.1”。高版本Liberty支持更多属性比如ccsn、ecsm但Library Compiler如果有license限制可能不解析这些扩展信息转换出的db只有传统NLDM时序。如果项目需要做先进节点时序signoff要用到ccs或ecsm库就要确保Library Compiler版本和feature足够否则精度不够。5. 效率提升经验把转库变成流水线5.1 一次配置全流程自动化转库这件事看起来简单但手工做很容易出错。我最后在自己环境里搭了一套流水线大体分三层Memory配置列表、转换脚本、验证脚本。memory配置列表是一个文本文件每行一个memory字段包括名称、类型、宽度、深度、mux、output register、功耗模式、corner。这个文件既给Memory Compiler用也给转库脚本用保证两边参数一致。Memory Compiler生成的lib名从配置文件里派生转库脚本读配置文件自动生成lib_list然后批量转db。这样做的好处是当架构改了某个memory深度只需要改配置列表然后重新跑一遍生成和转换脚本所有corner的db自动更新。再也不用手工一个一个敲命令。5.2 Memory db在综合阶段的验证闭环转库完成后我习惯先在一个测试顶层里做综合烟雾测试。DC里设置set target_library [list /path/to/dbs/sram_16k32_tt_1p2v_25c.db /path/to/stdcell/tt.db] set link_library [list * $target_library]然后read_verilog一个简单的wrapper只例化几个memory模块elaboratelink检查有没有Unresolved reference再跑一遍compile或update_timing。这一步能发现大部分库不匹配问题。综合后还要看下面积报告和时序报告。Memory的面积应该和lib里cell面积一致时序路径上和memory相关的delay应该在预期范围内。如果发现Q pin的transition特别大可能是lib里output load模型不对或者memory本身驱动能力不足。5.3 我的一些个人习惯最后聊几个个人习惯不算标准做法但能省不少事。第一给所有db文件加上时间戳或版本标记比如sram_16k32_tt_1p2v_25c_r1.db。这样team里好追溯不会出现拿错库跑结果的事。第二转换日志必须留档。脚本里用tee命令把lc_shell输出存成log一旦后面出现异常能快速定位是哪个库在哪一步出了问题。第三不要只盯着db文件的大小。很多memory的db几百KB几个corner看起来差不多实际上内容差异很大。要在DC里分别load并检查才能确保corner切换正确。第四Memory Compiler配置和转库任务别手动执行尽量写进Makefile或者持续集成CI里。我们后端flow每次跑回归前都会自动检查所有memory库的生成时间发现lib更新就会触发重新转库流程避免因为源文件更新导致db过期。做这行时间长了越来越觉得“从lib到db”虽然只是一个小环节但它卡在所有数字实现流程的最前面。源库质量不行后面综合、STA、功耗分析全是白跑。把Memory配置和Library Compiler的转换流程理顺配置文件收口脚本自动化回报率非常高。
返回列表