1. 为什么SRAM生成这件事值得单独写一篇工作流
如果你在数字后端或全定制流程里待过一段时间,大概率会遇到这样的场景:项目立项会上,架构师说"这个模块需要一块64KB的单口SRAM,位宽32,先跑起来看看面积和时序",然后所有人的目光就落到你身上。这时候你有两条路——要么去翻上一个项目遗留的SRAM宏单元碰运气,要么老老实实打开Memory Compiler从头生成一个。
我见过太多人在这条路上反复摔跤。不是因为他们不懂SRAM的工作原理,而是因为从"配置参数"到"ICC里能用的宏单元"中间隔着一整条工具链,每一环都有它自己的脾气。ARM Memory Compiler(后面简称ARM MC)算是目前业界用得最广的SRAM生成工具之一,最新版本在配置灵活性和输出完整性上比老版本好了不少,但它的输出物和Synopsys ICC之间的衔接依然需要一套清晰的操作流程。
这篇文章要讲的就是这套流程:从在ARM MC里配置一颗SRAM开始,到生成.lib/.lef/.gds/.v等一系列文件,再到用lef2milkyway把LEF转成ICC能吃的Milkyway库,最后在ICC里完成导入和验证。整套流程我会按实际操作顺序展开,每一步都告诉你为什么这么做、哪里容易出错、怎么验证做对了。
适合谁看?如果你是从RTL前端转后端的新人,或者一直在做综合但没碰过Memory Compiler,这篇文章能帮你把整条链路走通。如果你已经用过MC但每次导入ICC都出各种玄学问题,那里面关于lef2milkyway和库一致性检查的部分应该能帮你省下不少debug时间。
提示:本文基于ARM MC最新版的通用操作逻辑撰写,不同工艺节点(如16nm、22nm、28nm等)的MC界面和输出文件命名可能有细微差异,但核心流程一致。具体参数请以你手上的MC版本和工艺库文档为准。
2. 在ARM MC里配置一颗SRAM:参数背后的取舍逻辑
2.1 先搞清楚你要的是哪种SRAM
打开ARM MC之后,第一件事不是急着填参数,而是确认你要生成的SRAM类型。ARM MC通常支持这几种:
- Single-Port SRAM(SP SRAM):一个端口,读写共用。面积最小,适合做指令缓存、FIFO缓冲这类不需要同时读写的数据存储。
- Two-Port SRAM(TP SRAM):两个端口,一个专用于读,一个专用于写。注意它和Dual-Port不一样,TP的读端口不能写,写端口不能读。
- Dual-Port SRAM(DP SRAM):两个端口都能独立读写,适合做跨时钟域的数据交换或共享内存。
- ROM:只读,一般用于存固化的查找表或启动代码。
选型的时候有个经验法则:能用SP就不用TP,能用TP就不用DP。因为每增加一个端口,单元面积大概增加30%到50%,功耗也跟着涨。我见过有项目为了图省事全用DP,结果芯片面积比预期大了将近20%,回头改都来不及。
2.2 容量、位宽和深度的三角关系
确定类型之后,接下来是三个核心参数:总容量(Total Bits)、数据位宽(Word Width)、深度(Depth)。这三者的关系是:
总容量 = 位宽 × 深度比如你要做64KB的SRAM,位宽32bit,那深度就是:
64 × 1024 × 8 / 32 = 16384也就是16K的深度。但这里有个坑:ARM MC对深度和位宽的组合有约束,不是任意组合都能生成。通常深度必须是2的幂次或者某些特定值,位宽也有最小和最大限制。如果你填了一个不支持的组合,MC会直接报错或者自动帮你调整到最近的合法值。
我的建议是:先确定位宽,再算深度,最后看总容量是否满足需求。因为位宽往往由系统总线决定,比如32bit或64bit,而深度可以灵活调整。如果算出来的深度不是2的幂次,可以适当增加深度来凑整,多出来的地址空间不用就是了。
2.3 那些容易被忽略但很关键的配置项
除了容量和位宽,ARM MC里还有一堆配置项,新手很容易一路默认点过去,结果生成出来的SRAM在时序或功耗上不满足要求。以下几个值得特别关注:
Bank划分(Bank Number):大容量SRAM通常会被分成多个bank,每个bank有自己的译码电路。bank越多,单次访问的功耗越低(因为只激活一个bank),但面积会增大。一般深度超过4096之后,MC会建议你分bank。我的经验是:如果功耗不是特别紧张,用MC的默认bank数就行;如果要做低功耗设计,可以手动增加bank数,但要注意bank数增加会导致访问延迟略微上升。
流水线级数(Pipeline Stage):ARM MC允许你在SRAM内部插入寄存器,把读操作流水线化。比如设置1级流水线,读数据会在下一个周期才有效。这能显著提高SRAM的工作频率,但会增加读延迟。如果你的系统对读延迟不敏感但频率要求高,可以开流水线;如果要求单周期读出,那就不能开。
低功耗模式(Low Power Mode):包括Light Sleep、Deep Sleep、Shut Down等。这些模式通过控制内部电源开关来降低漏电。如果你的芯片有低功耗需求,记得在MC里使能这些模式,并在RTL里正确连接对应的控制信号。我踩过的坑是:MC里使能了低功耗模式,但RTL里没接控制端口,结果综合时报了一堆unconnected port的warning,后来才发现是忘了在例化时连接。
冗余与修复(Redundancy):大容量SRAM通常会加入冗余行或冗余列,用于提高良率。这个选项一般默认开启,但如果你做的是小容量SRAM或者对面积极度敏感,可以关掉。
2.4 配置完成后的检查清单
在点击"Generate"之前,建议对照下面这个清单过一遍:
| 检查项 | 确认内容 |
|---|---|
| 类型 | SP/TP/DP是否符合设计需求 |
| 容量 | 总bit数是否满足系统要求 |
| 位宽 | 是否与总线位宽匹配 |
| 深度 | 是否为合法值,地址位宽是否正确 |
| Bank数 | 是否合理,是否影响功耗和面积 |
| 流水线 | 是否需要,级数是否正确 |
| 低功耗 | 是否需要,控制信号是否已规划 |
| 冗余 | 是否开启,是否影响面积 |
| 工艺角 | 是否选择了正确的工艺角和电压 |
确认无误后,选择输出目录,点击生成。ARM MC会花几分钟到十几分钟不等(取决于SRAM大小和机器性能),生成一整套文件。
3. 生成物里到底有哪些文件,每个文件是干什么的
3.1 输出文件清单与用途
ARM MC生成完成后,输出目录里通常会有这些文件(不同版本命名可能略有差异):
- .lib:Liberty格式的时序库,包含SRAM的时序信息(setup/hold/access time等)。综合和STA都要用。
- .lef:Library Exchange Format,描述SRAM的物理信息,包括pin位置、金属层、blockage等。布局布线工具用。
- .gds:版图文件,最终tapeout要用。
- .v:Verilog行为模型,用于仿真。注意这是行为级模型,不是可综合的RTL。
- .db:Synopsys数据库格式,ICC可以直接读。
- .clf:Clock Library Format,某些版本会输出。
- .pdf/.html:数据手册,包含时序图、引脚说明、AC/DC特性等。
其中,ICC导入最核心的是.lef和.lib。.lef告诉ICC这个SRAM长什么样、pin在哪里;.lib告诉ICC这个SRAM的时序特性。两者缺一不可。
3.2 .lib文件里到底有什么
打开.lib文件,你会看到类似这样的结构:
library(sram_64kb_32bit) { cell(sram_64kb_32bit) { pin(A[13:0]) { direction : input; capacitance : 0.005; } pin(D[31:0]) { direction : output; function : "IQ"; } pin(CLK) { direction : input; clock : true; } ... timing() { related_pin : "CLK"; timing_type : setup_rising; rise_constraint(...) { ... } } } }关键信息包括:pin的方向、电容、时序约束(setup/hold)、延迟(access time)。STA工具会根据这些信息计算路径时序。如果你发现STA结果和预期不符,第一件事就是检查.lib里的时序值是否和MC里配置的一致。
3.3 .lef文件的结构解析
.lef文件描述物理信息,核心内容包括:
MACRO sram_64kb_32bit CLASS BLOCK ; ORIGIN 0 0 ; SIZE 500 BY 300 ; PIN A[13:0] DIRECTION INPUT ; PORT LAYER M2 ; RECT 0.1 0.1 0.2 0.2 ; END END A[13:0] ... OBS LAYER M1 ; RECT 0 0 500 300 ; END END sram_64kb_32bit- SIZE:SRAM的物理尺寸,单位是微米。
- PIN:每个pin的位置和所在金属层。
- OBS:obstruction,表示这个区域内不能布线。
ICC导入LEF后,会根据这些信息在布局时给SRAM预留空间,并在布线时避开OBS区域。
3.4 一个容易忽略的细节:pin的访问方向
LEF里pin的LAYER和RECT定义了pin在哪个金属层、什么位置。如果MC生成的LEF里pin只在M1层,而你的设计主要用M3走线,那ICC可能会报"pin access"问题。这时候需要在MC里配置pin的金属层,或者在后端手动调整。我的经验是:在MC里就把pin的金属层设成和周边逻辑一致的层,能省掉很多麻烦。
4. lef2milkyway:把LEF变成ICC能吃的格式
4.1 为什么需要这一步
ICC是Synopsys的工具,它读的物理库格式是Milkyway(.mw)。ARM MC输出的是LEF,两者格式不同,所以需要一个转换步骤。Synopsys提供了lef2milkyway这个工具来做这件事。
有人会问:ICC不是也能直接读LEF吗?确实,ICC支持在read_lef时直接读入LEF,但这种方式读入的LEF不会生成完整的Milkyway库,某些功能(如物理验证、天线检查)可能受限。更稳妥的做法是用lef2milkyway生成一个完整的.mw库,然后在ICC里作为参考库读入。
4.2 lef2milkyway的完整命令
基本命令格式如下:
lef2milkyway -lef sram_64kb_32bit.lef \ -layer_map /path/to/tech.tf \ -output sram_64kb_32bit.mw \ -lib_name sram_64kb_32bit \ -tech_lib /path/to/tech.mw参数说明:
-lef:输入的LEF文件路径。-layer_map:工艺的layer map文件(通常是.tf或.layermap),告诉工具LEF里的层名对应工艺里的哪一层。-output:输出的Milkyway库路径。-lib_name:库名,一般和SRAM名字一致。-tech_lib:参考的工艺库,通常是标准单元库的.mw。
4.3 常见报错与排查
报错1:Layer not found
Error: Layer M1 not found in layer map.原因:LEF里用的层名和layer map里的不一致。比如LEF里叫"METAL1",layer map里叫"M1"。解决办法是检查layer map文件,确保层名匹配。如果不匹配,可以手动编辑LEF或layer map。
报错2:Pin shape invalid
Error: Invalid pin shape for pin A[0].原因:LEF里pin的RECT坐标不合法,比如宽度为0或者坐标超出SIZE范围。这种情况通常是MC生成LEF时的bug,可以尝试重新生成,或者手动修正LEF。
报错3:Duplicate macro name
Error: Macro sram_64kb_32bit already exists.原因:输出目录里已经有同名的.mw库。删掉旧的或者换个输出路径即可。
4.4 转换后的验证
转换完成后,不要急着导入ICC,先做几个基本检查:
- 确认.mw目录存在且非空。
- 用
milkyway -lib sram_64kb_32bit.mw打开库,检查macro是否存在。 - 检查macro的SIZE是否和LEF里一致。
- 检查pin的数量和方向是否正确。
这一步花几分钟,能避免后面在ICC里浪费几个小时。
5. ICC导入SRAM宏单元的完整操作与验证
5.1 导入前的库准备
在ICC里导入SRAM之前,需要确保以下库已经准备好:
- 标准单元库(.mw):包含逻辑单元的物理和时序信息。
- SRAM的Milkyway库(.mw):刚才用lef2milkyway生成的。
- SRAM的.db库:从.lib编译而来,用于时序分析。
- 工艺库(.mw):包含金属层、通孔等信息。
在ICC的启动脚本里,通常这样设置:
set search_path [list . /path/to/stdcell.mw /path/to/sram.mw /path/to/tech.mw] set link_library [list "*" stdcell.db sram_64kb_32bit.db] set target_library [list stdcell.db sram_64kb_32bit.db]注意:SRAM的.db要放在link_library里,否则ICC找不到时序信息。
5.2 在ICC里读入SRAM
ICC读入SRAM有两种方式:
方式一:作为参考库读入
read_milkyway -lib_name sram_64kb_32bit.mw这种方式把SRAM库加载到内存,但不会自动例化到设计里。你需要在网表里例化SRAM,ICC会根据网表自动匹配。
方式二:在网表里直接例化
如果你的RTL里已经例化了SRAM的行为模型,综合后的网表里会有对应的实例。ICC读入网表后,会自动从link_library里查找对应的macro。
我一般用方式二,因为更符合实际流程。但要注意:综合时用的SRAM模型必须是.lib对应的.db,不能用行为模型,否则综合出来的网表里SRAM会被当成黑盒。
5.3 布局阶段的注意事项
SRAM在布局阶段有几个特殊之处:
- 方向(Orientation):SRAM通常有固定的pin方向,不能随意旋转。ICC里可以用
set_orientation来设置,但最好在MC里就确定好pin的方向,避免后端调整。 - HalO:SRAM周围需要留出halo(禁止布线区),防止周边逻辑的走线影响SRAM的pin access。ICC里可以用
create_placement_blockage来创建。 - 电源规划:SRAM的电源pin通常需要单独连接,不能和标准单元的电源网格混在一起。需要在power plan阶段单独处理。
5.4 时序验证:确认SRAM的时序被正确分析
布局完成后,跑一次report_timing,检查SRAM相关的路径是否被正确分析。重点看:
- SRAM的setup/hold是否满足。
- SRAM的access time是否在路径延迟里体现。
- 如果SRAM有流水线,检查流水线寄存器的时序。
如果发现SRAM的时序没有被分析,大概率是.db没有正确加载,或者link_library里缺少SRAM的.db。
5.5 物理验证:DRC和LVS
在tapeout之前,必须对SRAM做DRC和LVS检查。SRAM的GDS是从MC生成的,通常已经通过了DRC,但在集成到顶层后,可能会因为周边走线或电源连接引入新的DRC violation。LVS则要确认SRAM的网表连接和版图一致。
我的经验是:在ICC里做完place之后就跑一次DRC,不要等到route完再跑。因为SRAM周边的DRC问题往往在place阶段就能发现,早发现早修复。
6. 那些年我踩过的坑与实战心得
6.1 坑一:MC版本和工艺库不匹配
有一次我拿了一个28nm的MC,却用了22nm的工艺库,结果生成的LEF里层名和工艺库对不上,lef2milkyway直接报错。后来才发现是MC的工艺配置文件选错了。教训:生成之前一定要确认MC的工艺节点和实际工艺库一致。
6.2 坑二:.lib里的时序单位搞错
ARM MC生成的.lib里,时间单位可能是ns,也可能是ps。如果STA工具默认的单位和.lib里的不一致,时序分析结果会差1000倍。我遇到过有人把ps当成ns,结果时序全过,tapeout后芯片跑不起来。教训:打开.lib文件,确认time_unit字段的值。
6.3 坑三:lef2milkyway的layer map用错
layer map文件通常由工艺厂提供,但有时候会有多个版本(比如不同金属层数的)。如果用错了版本,转换出来的.mw库可能缺少某些层的信息,导致ICC布线时报错。教训:确认layer map和工艺库版本一致。
6.4 坑四:SRAM的pin没有正确连接
综合后的网表里,SRAM的某些控制pin(如低功耗模式的sleep pin)可能被悬空。如果MC里使能了低功耗模式但RTL里没接,综合工具会报warning,但很多人忽略。到了ICC里,这些悬空的pin会导致LVS错误。教训:综合后检查网表,确认SRAM的所有pin都有连接。
6.5 一个提高效率的小技巧
如果你需要生成多个不同配置的SRAM,可以写一个脚本批量调用ARM MC。ARM MC通常支持命令行模式,你可以把配置参数写在一个文件里,然后批量生成。这样比在GUI里一个个点快得多,而且不容易出错。
# 示例:批量生成脚本 for config in config1 config2 config3; do mc -f ${config}.cfg -o ./output/${config} done生成完成后,再用脚本批量跑lef2milkyway,最后在ICC里一次性导入。整套流程自动化之后,原来需要半天的工作量可以压缩到半小时以内。
6.6 关于SRAM和DRAM的区别,顺便说一句
有人可能会问:为什么不用DRAM?DRAM密度高、成本低,但需要刷新电路,而且速度慢。SRAM不需要刷新,速度快,但面积大、成本高。在芯片内部,SRAM通常用于缓存和小容量存储,DRAM用于大容量主存。如果你在做SoC,片内SRAM是标配,片外DRAM才是大容量存储的选择。
至于大容量SRAM内部的寻址电路和读写电路,简单说:地址译码器把地址信号转换成行选和列选,行选激活对应的字线(word line),列选通过位线(bit line)读出或写入数据。大容量SRAM通常采用分级译码和bank划分来降低译码延迟和功耗。这些细节在MC里都是自动处理的,你不需要手动设计,但理解原理有助于你在配置参数时做出正确的取舍。
7. 写在最后的一些个人体会
整套流程走下来,最深的体会是:Memory Compiler不是黑盒,它的每一个输出文件都有明确的用途,每一步转换都有它的道理。很多人觉得SRAM生成很神秘,其实拆开来看就是"配置→生成→转换→导入→验证"这五步。每一步都做扎实,后面就不会有玄学问题。
另外,工具版本的一致性怎么强调都不为过。MC版本、工艺库版本、ICC版本、lef2milkyway版本,任何一个不匹配都可能引入难以排查的问题。我的习惯是在项目开始时就锁定所有工具的版本,并记录在项目文档里,避免中途换版本导致意外。
最后分享一个检查清单,每次导入SRAM后按这个过一遍,基本不会出大问题:
| 检查项 | 方法 |
|---|---|
| .lib时序单位 | 打开.lib看time_unit |
| .lef层名 | 和layer map对比 |
| .mw库完整性 | milkyway打开检查 |
| ICC link_library | 确认包含SRAM的.db |
| 网表pin连接 | 检查所有pin是否有连接 |
| 布局halo | 确认SRAM周围有blockage |
| 时序分析 | report_timing看SRAM路径 |
| DRC/LVS | place后先跑一次 |
这套流程我在多个项目上用过,从28nm到16nm,从SP SRAM到DP SRAM,基本都能顺利跑通。希望对你有所帮助。如果遇到什么奇怪的问题,欢迎一起讨论。