第一次把IC Compiler II(下面直接叫ICC2)装好,拿到新工艺PDK,兴冲冲打开icc2_shell准备跑第一个block,结果发现事情没想象中那么简单:老ICC里靠create_mw_lib建立物理库那套流程,在ICC2里已经被create_workspace取代了。很多从旧流程迁移过来的工程师,第一反应是去读design,或者直接找create_block,结果卡在workspace这个概念上好几天,连一个空的block都建不起来。
这篇文章写给准备上手ICC2,或者已经被workspace绕晕的工程师。我会从create_workspace这条命令入手,讲清楚它为什么存在、每个参数背后到底在干什么、实际项目里怎么搭一套不出错的创建序列,以及我在多个项目里踩过的那些坑。内容偏流程和运维视角,不是help文档的复读,读完你至少能自己拼出一个能跑的workspace创建脚本。
1. 先搞清楚:create_workspace 在 ICC2 里到底扮演什么角色
1.1 workspace 本质上是“装载设计环境的内存容器”
很多刚接触ICC2的人会把create_workspace理解成“打开一个设计文件”,这个理解不准确。workspace更像是一个带齐图纸、工具和分工表的会议室:每个block对应一个workspace,里面放着这个block的MMMC配置、加载好的参考库、当前打开的floorplan、各种option状态。create_workspace干的事情,是把这一整套分析环境装载到内存里,而不是单纯读一个.ndm或者.db文件。
打个比方,你用文本编辑器打开一个文件,你还需要一套语法高亮、编译器路径、工程配置;ICC2也一样,它在分析你的网表和版图之前,必须先知道工艺信息、库信息、corner信息。这些信息不提前装载好,后面的place_opt、route_opt根本没有办法跑。所以ICC2把“装载环境”这一步单独做成了命令,也就是create_workspace。
1.2 为什么ICC2要把workspace单独拎出来,而不是像老ICC那样全局一套库
老ICC时代,design和库的关系是“全局绑定”,你打开工具,库就在那儿,全芯片只有一个上下文。ICC2要支撑多block并行、多corner分析、分布式跑法,这种全局模型不够用。每个block可能需要不同的库版本、不同的约束、不同的scenario,如果放在同一个全局上下文里,并行跑两个block很容易互相污染。
workspace机制就是为了隔离。每个workspace有独立的library resolution、独立的MMMC上下文、独立的option状态。好处很明显:并行跑几十个block互不干扰;坏处也很明显:很多人不理解“为什么我只是想看一眼版图,还要先create_workspace”。实际上,你哪怕只是打开一个已经存在的设计,也得先有一个workspace去承载它——这正是create_workspace存在的价值。
1.3 create_workspace 和 create_block 不是一回事,别搞混
这是新手最容易搞混的地方。create_workspace管的是环境,create_block管的是设计对象本身。实际工程里最常见的标准动作是:
create_workspace -name blockA -block_reference_libraries $ref_libs -tech_file $tech_file create_block -name blockA -tech_file $tech_file current_block blockA先用create_workspace搭好环境,再用create_block创建一个空白block,或者用open_block/读入方式加载已有block。有些版本的ICC2里,create_workspace带-block参数可以直接打开已有数据,这时候就不需要再显式create_block。但不管哪种方式,你的第一步永远是先把workspace立起来。
2. create_workspace 参数全拆解:每个选项背后的实际用途
2.1 库类型分组:为什么ICC2把库拆得那么细
create_workspace有一组看起来让人头大的库参数,实际用熟了之后会发现,这个拆分是合理的。我常用的版本核心选项大致长这样:
create_workspace -name workspace名称 -block_reference_libraries block参考物理库(.ndm/.db) -tech_file 工艺文件(.tf) -link_libraries 逻辑链接库(.db) -timing_libraries 时序分析库 -physical_libraries 物理分析库 -si_libraries 信号完整性分析库 -post_route_drc_libraries 布线后DRC库 -delay_corner 延迟corner -operating_condition 工作条件 -mode 约束模式 -scenario scenario列表 -block 指定已有block名称不同版本选项名称可能略有差异,有的写成-timing,有的写成-timing_libraries,用之前先敲help create_workspace确认一下,这个习惯能帮你省不少事。
为什么要分这么细?因为ICC2在做综合、floorplan、布线、signoff时,对库的需求完全不同。提前按用途把库归类好,工具就不用每次临时去找,也方便针对不同分析目标做取舍。比如post-route DRC阶段要用的库和综合阶段要用的库就经常不一样,分开指定可以避免加载了多余的库导致内存浪费。
2.2 库参数之间的关系:参考库 vs 链接库
-block_reference_libraries和-link_libraries是最容易混淆的两个参数。简单说:
-block_reference_libraries是给block物理实现用的,提供floorplan、macro、memory的物理形状信息,通常是NDM格式(老流程里是MW)。-link_libraries是给网表链接用的,用来解析netlist里的cell reference,通常是.db格式的逻辑库。
实际项目中这两种库都要给。如果只给参考库没给链接库,网表里的std cell解析不出来;只给链接库没给参考库,版图上没有cell形状,floorplan根本画不了。我见过最典型的报错是:
Error: Can't find technology file ... Error: Can't load reference library ...这种基本都是库参数不完整导致的。排查的时候先别慌,一个一个确认:tf路径对不对、ndm路径对不对、link库的.db路径对不对,90%的问题出在路径拼写上。
2.3 MMMC相关参数:scenario、mode、corner怎么联动
MMMC(multi-mode multi-corner)是ICC2的核心概念之一。create_workspace里有一组和MMMC联动的参数:-mode、-scenario、-delay_corner、-operating_condition。
很多flow的写法是,先在脚本里用create_library_set、create_delay_corner、create_constraint_mode、create_scenario定义好所有MMMC对象,再在create_workspace里用-scenario指定要装载哪些scenario。工具会做一个“快照”式的关联,把指定的scenario连同背后的library set和corner一起装进workspace。
如果create_workspace里不提供任何MMMC参数,工具在创建workspace后会自动生成默认的scenario和mode。这看起来很方便,但坑在于:默认的mode和corner没有任何时序库关联,你后面跑时序分析会发现库是空的。所以正式流程里,我几乎不用默认MMMC,都是显式指定。
2.4 冷门参数也有大用处:browse_mode、floorplan参数、block参数
有些参数平时用不到,但特定场景下能救命。
-browse_mode是一个很多人不知道的好东西。它只加载物理信息,不加载时序库,适用于快速浏览版图结构、确认macro摆放、检查电源网络。打开速度快、内存占用小。但注意,browse_mode下不能跑时序分析,也不能做任何优化,只能看。
-floorplan_ref_library和-floorplan_name用于指定已有的floorplan模板。如果你的项目有标准的floorplan模板库,可以在创建workspace时直接指定,省得进去之后再手动导入。
-block参数用于打开已经保存的block的workspace上下文。相比从零创建,指定-block会把之前保存的analysis context一起恢复,包括加载好的库、场景、约束,非常适合接着上次的进度继续干活。
3. 真实项目中的创建顺序:一套能直接落地的命令序列
3.1 从清晰的变量定义开始
写项目脚本我习惯把所有路径和库名先存成变量,再传给命令。这样既方便维护,也能在日志里清楚地定位到底用了哪些文件。
set block_name "cortex_a53_top" set tech_file "/pdk/tsmc7ff/tf/tsmc7ff_9T.tf" set ref_libs [list \ "/pdk/tsmc7ff/ndm/tsmc7ff_sc9t_base.nda" \ "/pdk/tsmc7ff/ndm/tsmc7ff_sram_2k.nda" \ "/pdk/tsmc7ff/ndm/tsmc7ff_io.nda" \ ] set link_libs [list \ "/pdk/tsmc7ff/db/tsmc7ff_sc9t_ffg0p88v0p88v_125c.db" \ "/pdk/tsmc7ff/db/tsmc7ff_sc9t_ssg0p72v0p72v_125c.db" \ ] set timing_libs [list \ "/pdk/tsmc7ff/db/tsmc7ff_sc9t_ffg0p88v0p88v_125c.db" \ "/pdk/tsmc7ff/db/tsmc7ff_sc9t_ssg0p72v0p72v_125c.db" \ ]变量准备好之后,再用create_workspace就清爽很多:
create_workspace -name $block_name \ -block_reference_libraries $ref_libs \ -tech_file $tech_file \ -link_libraries $link_libs \ -timing_libraries $timing_libs这里有个容易被忽略的点:tech_file在create_workspace和create_block里都可能出现。我的经验是,create_workspace阶段就给 tf,可以把工艺信息和workspace绑定,后续创建block时不需要重复给。但如果你的flow里不同block用不同tf,那就在create_block里单独指定,灵活处理。
3.2 MMMC配置的时机:先定义对象,再创建workspace
实际项目flow里,最常见的做法是先在icc2_shell里source一个mmmc.tcl,把所有library_set、delay_corner、constraint_mode、scenario都定义好,然后再执行create_workspace -scenario。这样workspace创建后自动带上完整的MMMC分析上下文,进去就能直接干活。
一个典型的mmmc.tcl片段:
create_library_set -name lib_fast \ -timing [list "/pdk/tsmc7ff/db/tsmc7ff_sc9t_ffg0p88v0p88v_125c.db"] create_library_set -name lib_slow \ -timing [list "/pdk/tsmc7ff/db/tsmc7ff_sc9t_ssg0p72v0p72v_125c.db"] create_delay_corner -name dc_fast -library_set lib_fast create_delay_corner -name dc_slow -library_set lib_slow create_constraint_mode -name func_mode \ -sdc_files [list "/proj/blockA/constraints/blockA_func.sdc"] create_scenario -name func_fast \ -delay_corner dc_fast -constraint_mode func_mode create_scenario -name func_slow \ -delay_corner dc_slow -constraint_mode func_mode然后在主脚本里:
source -echo /proj/blockA/scripts/mmmc.tcl create_workspace -name $block_name \ -block_reference_libraries $ref_libs \ -tech_file $tech_file \ -link_libraries $link_libs \ -scenario {func_fast func_slow}这种做法的好处是,MMMC对象定义和workspace创建分离,方便调试。如果MMMC文件里有错,source阶段就会报错,不会等到workspace创建半天之后才发现scenario不对。
3.3 首次导入已有数据:拿到网表和SDC之后的正确姿势
如果是新设计,工作流通常是:创建workspace → 创建block → 读入网表 → 读入约束 → 检查环境。
create_workspace -name $block_name \ -block_reference_libraries $ref_libs \ -tech_file $tech_file \ -link_libraries $link_libs \ -scenario {func_fast func_slow} create_block -name $block_name current_block $block_name link_block -netlist /proj/blockA/netlist/blockA.v read_sdc /proj/blockA/constraints/blockA_top.sdc check_workspacecheck_workspace是个值得养成习惯的命令,它会检查当前workspace的完整性,比如库引用是否正确、约束是否完整、是否有missing cell。我第一次跑新block时一定会执行一次,把报出来的warning全部过一遍再继续。
4. 最容易翻车的几个坑:真实报错与完整排查链路
4.1 库加载warning被忽略,后面冒出一堆“幽灵违例”
有次项目跑到布线后DRC阶段,一个完全没动过的宏突然报出天线违例,位置和形状都莫名其妙。当时第一反应是DRC rule设置有问题,查了半天没结果。最后回溯到workspace创建日志,发现一开始就有一行warning:
Warning: Cell SRAM_2K is defined in multiple reference libraries. The last one will be used.问题出在-block_reference_libraries里同时给了两个版本不一致的NDM库,其中一个旧库里的SRAM_2K物理尺寸和新库不一致。工具当时没报错,只给了warning,但物理上用的却是错误版本,后续所有分析都基于这个错误物理信息。
排查链路:看到异常DRC违例,别急着改约束或者调antenna rule,先回看workspace创建日志,搜warning/conflict/redefinition关键词。一旦发现“multiple libraries”字样的warning,立刻检查库列表,把重复的cell库清理掉。库版本混用这种事,在多人协作、多项目复用的环境里特别容易发生。
4.2 scenario名字拼错,工具居然不报错
另一个隐蔽的坑是MMMC scenario名字输错。有一次脚本里create_scenario定义的名字是func_fast_125c,但create_workspace -scenario写成了func_fast,工具没有报错,只是静默地少加载了一个scenario。后续跑报告时,report_scenarios只列出了func_fast_125c,没觉得异常;直到report_qor发现少了 corner 数据,才回头去查。
这个坑在于,ICC2对-scenario里找不到的名字,有时候只是warning,有时候直接忽略,不会fail整个命令。排查链路:创建workspace后第一件事就是敲get_scenarios检查当前加载了哪些场景,对照MMMC定义检查是否有遗漏。我在项目脚本里固定写一段:
set expected_scenarios {func_fast_125c func_slow_125c func_typical_25c} set loaded_scenarios [get_scenarios -quiet] foreach scen $expected_scenarios { if {[lsearch $loaded_scenarios $scen] < 0} { error "Missing scenario: $scen" } }脚本里加启动断言,比事后发现corner缺失要高效得多。
4.3 browse_mode打开workspace后不退出,导致后续操作异常
团队里有同事喜欢用icc2_shell -browse_mode开workspace查版图,查完不退出,挂在服务器上。过了几天,另一个人在同一目录下跑正常模式创建workspace,结果提示workspace已经被锁定或状态异常,甚至出现MMMC配置被覆盖的情况。
browse_mode设计出来是为了快速查看,但它也会在workspace目录下写状态文件。如果你用browse_mode打开后又在这个session里做了保存动作(哪怕只是保存了workspace options),就会污染正常流程。排查链路:遇到workspace状态异常,先查这个目录下有没有多个历史session文件,把无关的browse session清理掉。我现在的规矩是:browse_mode只用来“看”,绝不在里面做任何保存,看完立刻退出。
4.4 重复source MMMC文件导致的同名对象
flow脚本写好之后,难免要反复调试。问题出在mmmc.tcl被source了多次,create_library_set里的对象名已经存在,工具不会报错,只会提示:
Warning: Library set 'lib_fast' already exists. The existing object will be used.然后后面定义的lib_slow可能因为某种原因覆盖了lib_fast的关联,导致corner指向了旧对象。这种问题非常隐蔽,因为warning一闪而过,不仔细看根本发现不了。
排查链路:get_library_sets输出所有对象,看看有没有重复定义;在MMMC脚本开头加保护逻辑:
if {[sizeof_collection [get_library_sets -quiet]] > 0} { remove_library_sets [get_library_sets -quiet] }每次source前先清理,再重新定义,这样能保证MMMC对象的唯一性。这个习惯帮我省了很多莫名其妙的corner错误。
4.5 变量名引用问题:一个导致整条命令失效的细节
还有个很小但很坑的问题:create_workspace -name传变量时,如果变量是列表,而不是字符串,workspace名字会变成一串奇怪的东西。有一次我写:
set block_name [list "blockA" "blockB"] create_workspace -name $block_name ...结果workspace名字直接变成{blockA blockB},后面的命令全乱了。排查链路:加载workspace前先echo $block_name确认变量内容。变量传递这种基础细节,往往是最容易浪费半天时间的地方。
5. 与周边命令的搭配:create_workspace 不是一条孤立命令
5.1 用 get_workspace_options 检查当前环境配置
创建完workspace后,如果想确认当前环境用的什么库、什么配置,可以用get_workspace_options查看。它能给出当前workspace的各种option状态,包括参考库路径、tech file路径等。在调试“为什么有的库没加载进去”的时候,比翻再多的日志都管用。
我一般会写一个小脚本:
proc check_ws {ws_name} { set opts [get_workspace_options $ws_name] puts "Workspace: $ws_name" puts "Reference libraries: [get_attribute $opts block_reference_libraries]" puts "Tech file: [get_attribute $opts tech_file]" }然后项目里统一跑一遍,确认所有block的workspace配置一致。这在多人协作时特别重要——你永远不知道别人在本机加载了哪些奇怪的库。
5.2 保存与恢复:save 和 create_workspace 的配合
ICC2里保存数据用得最多的是write_design或save_block,把 block 数据落到磁盘。下次要继续干活时,不需要傻乎乎地重新创建workspace,直接用create_workspace -name $block_name -block $block_name就能把之前的workspace上下文一起恢复。
这里有个经验:workspace上下文里保存的不只是design数据,还有当时加载的库、scenario、option状态。用-block参数恢复,比手工重新source一堆脚本要快得多,也可靠得多。但前提是保存时的环境是干净的,所以我习惯每次保存前跑一次check_workspace。
5.3 批处理流程中的workspace管理:并行任务千万别共享
ICC2在实际项目里很少单跑一个block,经常是几十个任务并行。并行时最容易犯的错误是多个任务复用同一个workspace目录,导致库加载冲突、session文件互相覆盖。我现在的做法是:
- 每个任务单独建一个工作目录,目录下再建对应的workspace。
- 所有路径在脚本里用绝对路径,不用相对路径——分布式跑法下相对路径特别容易出事。
- 批处理脚本里设置
set_workspace_options禁止自动保存,防止并行任务把别人的workspace覆盖掉。
set_workspace_options -allow_auto_save false5.4 把workspace创建封装成proc,统一团队入口
项目走到中期,我习惯把整个workspace创建过程封装成一个proc,放在公共脚本库里:
proc create_ws_from_template {block_name} { source -echo /proj/common/scripts/mmmc.tcl set ref_libs [get_standard_ref_libs] set tech_file [get_standard_tech_file] create_workspace -name $block_name \ -block_reference_libraries $ref_libs \ -tech_file $tech_file \ -link_libraries [get_standard_link_libs] \ -scenario [get_standard_scenarios] check_workspace }这样每个新人都能通过同一个入口创建工作区,不会拿着五花八门的参数乱试。团队内统一入口之后,很多因为个人配置差异导致的灵异问题都消失了。
6. 最后再分享一点个人体会
用ICC2这些年,我最有感触的不是哪条命令多么复杂,而是这工具有很多“不报错但会坑你”的地方。create_workspace是每个ICC2项目的第一个门槛,恰恰是第一步最容易出错。库路径写错、scenario名对不上、变量引用有问题、browse session残留,这些问题单看都不难,但叠在一起就非常消耗精力。
我现在自己接手一个新block,流程固定成这样:先check环境变量 → source MMMC → create_workspace → 马上get_scenarios和check_workspace→ 再看log里有没有warning/conflict/missing。走完这一套才继续后面的floorplan或placement。这个习惯帮我挡掉了不少本可以避免的返工。
如果你刚接触ICC2,别急着研究复杂的multi-corner优化脚本,先把create_workspace跑顺。这条命令理顺了,后面create_block、link_block、读约束、跑floorplan,都会顺很多。