Innovus 18.1/19.1 CCOpt时钟树配置调试与验收 简介面向使用 Cadence Innovus 进行物理实现的 IC 设计工程师这份文档聚焦复杂时钟网络的配置与调试以 CCOpt 为工具解决时钟树构建、平衡与抖动控制中的实际问题适合具备一定 CTS 基础、需要处理多时钟域与时序收敛难题的中高级读者。整份资料只有 1 个 PDF 文件约 1.3MB内容涵盖准备工作与推荐配置、CCOpt 主要流程阶段及各阶段可调试内容、基础检查与时钟树调试器用法、时钟路径追踪与跨视图探测、报告命令与属性查询并延伸到架构级时钟控制与 sink 修改如屏蔽数据通路、刻意偏斜、平衡不相交时钟树等专题。目前已有 211 人学习下载。文档可帮助读者在 Innovus 18.1/19.1 环境下建立从约束设置到问题定位的完整调试思路对照流程阶段逐项排查时钟树瓶颈与延迟不均是一份可直接用于工程实践的参考手册。1. 装之前先认清这套环境Innovus 18.1/19.1 跑 CCOpt 的真实门槛有个坑我见过不止一次环境装好了innovus能起license 也拿得到脚本从旧项目拷过来直接跑 CTS结果天亮一看时钟树上插的全是库里标了 dont_use 的低阈值单元面积和功耗双双超标。原因不在工具在版本Innovus 18.1 之前CCOpt 里 cell 属性指定的单元会盖掉库定义的 dont_use从 18.1 起用户自己打的 dont touch 才被真正尊重。这就是为什么在 Cadence Innovus 的 CTS 阶段装什么版本、按什么顺序配参数比会不会敲命令重要得多。复杂时钟网络的环境本来就难装多时钟域、深 clock gating、macro feedthrough 混在一起库和 SDC 里的设置又都是奔着 signoff 去的拿来直接建树几乎不可能最优。这篇笔记按我实际搭环境的顺序走一遍从目录、license、启动脚本到 CCOpt 的 properties、阶段断点、调试点再到翻车了怎么救最后落到怎么验证这套环境真的跑通了。2. 环境落地Innovus 18.1/19.1 装在哪个路径、license 怎么指、启动脚本怎么写2.1 版本选型18.1 与 19.1 的取舍先定死先把版本定下来再动手不要装完才发现脚本语义对不上。这份应用笔记覆盖的是 Innovus 18.1 和 19.1 两个产品版本流程主线一致差异集中在几个地方关注点Innovus 18.1Innovus 19.1落地建议用户 dont touch开始被尊重同样尊重低于 18.1 的版本别用来跑复杂时钟CCOpt 单元列表 vs 库 dont_usecell 属性优先于库定义同左版本再新也要显式给 cell listStylus(CUI) 与 legacy 命令两套并存两套并存一个脚本只走一套别混写应用笔记里的调试章节覆盖覆盖按同一套流程走即可我一般的做法是跟项目 signoff 用同一个大版本工艺库、LEF、时序库按版本单独挂载不要两个版本共用一份 lib。跨版本复用库文件出问题时你连是库的问题还是工具的问题都分不清。还有一个必须提前确认的点CUI 与 legacy 命令的差别。CUICommon User Interface也叫 Stylus是新的数据库访问和命令体系这份笔记里 CUI 代码和旧版代码是并列给出的。同一份脚本里混用最常见的结果是后写的设置把前面的覆盖掉而且不报错——这种玄学问题查起来最费时间。2.2 安装目录与磁盘布局安装本身没什么花活难的是布局。我习惯把工具、库、run 目录彻底分开工具目录只读run 目录按项目分# 工具只读挂载按版本并列方便共存和回滚 /opt/eda/innovus/18.1 /opt/eda/innovus/19.1 # 工艺库只读多项目共享按工艺节点分目录 /proj/lib/tech/lef /proj/lib/tech/timing /proj/lib/tech/ndr # 运行区可写按项目和迭代编号 /proj/project/run/cts_r1 /proj/project/run/cts_r2 # 临时区必须放本地盘不要放网络盘 /scratch/$USER/innovus用mkdir -p一次建好权限上工具和库给读执行、不给写sudo mkdir -p /opt/eda/innovus/19.1 sudo chown -R eda:eda /opt/eda/innovus sudo chmod -R 755 /opt/eda/innovus mkdir -p /scratch/$USER/innovus /proj/project/run第一段建目录并改属主第二段保证工具目录对所有使用者可读可执行、不可写——这条不是形式主义工具目录被误写过一次后面所有人的 run 都会莫名其妙失败。临时区单独拎出来是因为 cluster 和 trial 阶段会频繁写中间数据库放在 NFS 上能把一个两小时的 CTS 拖成一下午。2.3 环境变量与 license 指向环境变量脚本按版本各写一份需要哪个 source 哪个不要塞进.bashrc全局生效# /opt/eda/env/innovus-19.1.sh export INNOVUS_HOME/opt/eda/innovus/19.1 export PATH$INNOVUS_HOME/bin:$INNOVUS_HOME/tools/bin:$PATH export LD_LIBRARY_PATH$INNOVUS_HOME/lib:$INNOVUS_HOME/tools/lib:${LD_LIBRARY_PATH} # license指向你们自己的 license 服务端口不要写死在个人 shell 里 export CDS_LIC_FILE5280license-host # 强制 64 位复杂时钟设计 32 位根本吃不住 export CDS_AUTO_64BITALL # 临时目录放本地盘 export TMPDIR/scratch/$USER/innovus逐行说明一下取舍。PATH里bin和tools/bin都加上是因为不同子工具的可执行文件不在同一层LD_LIBRARY_PATH必须显式给不然在容器或集群节点上会去链系统自带的同名库报出来的错跟实际原因差十万八千里。CDS_LIC_FILE写成端口主机的形式具体端口按你们 license 服务的实际配置改别照抄。TMPDIR这行最容易被忽略但它是 cluster/trial 阶段性能差异的主要来源之一。装完先验一次别急着跑设计source /opt/eda/env/innovus-19.1.sh which innovus # 确认指向的是你 source 的那个版本 innovus -version # 确认版本号和团队约定一致which和-version两条都要看。前者确认路径优先级没被别的脚本抢走后者确认 license 起来之后拿到的确实是目标版本——有些环境里PATH对了但 license 队列给的是另一个版本光看which发现不了。2.4 启动脚本批处理跑别在 GUI 里调复杂时钟的调试确实要开图形界面看时钟树调试器但正式跑一律走批处理日志落到文件里出问题能回看# run_ccopt.tcl —— 顶层批处理脚本骨架 setMultiCpuUsage -localCpu 16 ;# 按机器核数给cluster 阶段吃多核 setDesignMode -process 16 ;# 工艺节点按项目实际改 read_mmmc mmmc.tcl ;# 多模式多端角先读约束再读网表 read_physical -lef {tech.lef macros.lef} read_netlist top.v init_design # 后面接第 3 章的 CCOpt 配置配置完再进建树setMultiCpuUsage给的是本地核数cluster 阶段并行度直接决定这一段跑多久read_mmmc放在init_design之前是为了让约束模式和端角在建库阶段就确定下来避免后面改模式导致时钟被重新提取一遍。innovus -no_gui -files run_ccopt.tcl -log cts_r1.log-no_gui是硬性要求图形模式下跑大批量设计内存占用会翻倍-files指定脚本-log把 stdout 落到文件后面排查基本靠它。3. 运行前配置CCOpt 的 cell list、target、sink_type 三件事必须先配3.1 为什么不能让工具从库和 SDC 里自动提取CCOpt 本身有能力从库、设计和时序约束里推断出建树需要的全部选项。问题在于库和 SDC 里的这些值大多是奔着 signoff 去的拿来建树基本不会是最优解。显式给一遍是这套环境能不能跑出合理结果的分水岭。提示在花时间研究结果、试各种冷门选项之前先确认基础设置和设计一致、并且设在正确的位置。时钟提取这一步会被全局属性和挂在具体 pin 上的属性共同影响位置设错了后面所有调试都是白费。3.2 post-CTS 优化与 OCV 设置这一段只要最小集合把对完整时钟配置有影响的约束模式抓进来就行# ---- CUI / Stylus 写法 ---- # 关闭 latency 修改chip level 和复杂架构设计里必须关否则 IO 延迟被改掉 set_ccopt_property update_io_latency false # OCV 分析模式 set_db timing_analysis_type ocv # ---- Legacy 写法等价 ---- set_db cts_update_clock_latency false setAnalysisMode -analysisType onChipVariationupdate_io_latency这行是重点。芯片级设计里 IO 的延迟通常由顶层统一规划CTS 阶段一旦去改它后面时序对不上还找不到原因属于那种跑完看着没问题、到顶层全崩的坑。OCV 模式则决定了延迟计算走哪套模型delaycal系列属性按你们实际用的 OCV 配置补齐具体属性名以本地set_db的 help 输出为准别照抄别人的脚本。3.3 路由规则时钟网络必须走非默认规则时钟网络用非默认规则NDR是常规操作宽线、双倍间距为的是控制寄生和串扰# 先定义一条时钟专用的路由规则 create_route_type -name clock_ndr_1 \ -non_default_rule clock_2w2s \ -top_preferred_layer M6 \ -bottom_preferred_layer M4 # 按网络层级分别挂上去top / trunk / leaf set_ccopt_property route_type -net_type {top trunk leaf} clock_ndr_1 # Legacy 等价写法 set_db cts_route_type_top clock_ndr_1 set_db cts_route_type_trunk clock_ndr_1 set_db cts_route_type_leaf clock_ndr_1-non_default_rule指向工艺库里的 NDR 定义名这个名字必须和库文件里完全一致写错了不报错、只是规则不生效。-top_preferred_layer和-bottom_preferred_layer限定走线层范围时钟网络一般避开低层走线以减少耦合net_type三个层级分开挂是因为 trunk 和 leaf 对线宽、间距的要求并不一样一刀切会让 leaf 段过度占用布线资源。3.4 单元列表空列表的特殊含义与过滤原因排查单元选择直接决定时钟树长什么样# CUI / Stylus set_ccopt_property buffer_cells {BUF_X2 BUF_X4 BUF_X8 BUF_X12} set_ccopt_property inverter_cells {INV_X2 INV_X4 INV_X8} set_ccopt_property clock_gating_cells {CKLNQD1 CKLNQD2 CKLNQD4} set_ccopt_property logic_cells {AND2_X1 OR2_X1 MUX2_X1} # Legacy 等价 set_db cts_buffer_cells {BUF_X2 BUF_X4 BUF_X8 BUF_X12} set_db cts_inverter_cells {INV_X2 INV_X4 INV_X8} set_db cts_clock_gating_cells {CKLNQD1 CKLNQD2 CKLNQD4} set_db cts_logic_cells {AND2_X1 OR2_X1 MUX2_X1}这里有个必须知道的语义空列表不是什么都不用而是所有单元都进来——不管阈值电压族、不管驱动能力全被纳入候选。这显然不是你想要的结果但它是默认行为脚本里漏写一行就是灾难现场。另外如果设计里有常开always-on单元必须显式加进列表工具不会替你补。时钟门控单元和逻辑单元一起给是为了让它们也参与优化能显著减少插入的 buffer 数量。把列表传进去之后工具还会做一层过滤和裁剪传进去的单元不一定全被用上。查过滤原因report_ccopt_cell_filtering_reasons ;# CUI report_cts_cell_filtering_reasons ;# Legacy这两条报告是排查我明明给了某个 buffer为什么树上一根都没有的第一入口别跳过。另外从 18.1 起用户设的 dont use 列表会无条件生效不管这些单元是否出现在 CTS 的 cell list 里。实例级别还可以设成 size-only给工具留更多调整空间。3.5 target_max_trans 与 target_skew影响面积和功耗的两个旋钮这两个目标值强烈建议显式设置它们不只看最终结果还会影响 cluster 阶段的分组做结构性调试时尤其要设准set_ccopt_property target_max_trans 0.120 ;# 单位跟随设计的时间单位 set_ccopt_property target_skew 0.080 # Legacy set_db cts_target_max_transition_time 0.120 set_db cts_target_skew 0.080target_max_trans在低功耗设计里格外重要它直接决定时钟网络要做多粗——放宽一点缓冲器数量和面积能降一大截同时流程仍然会避开库规定的 DRV 限制不会真的违例。target_skew相反给得太紧会触发过度平衡工具为了压那几十皮秒拼命加缓冲、做长路径最后面积和功耗都上去了实际收益为零。我一般从 SDC 推出的值再放宽 20% 起步看结果再收。3.6 sink_type决定时钟网络在哪里停sink_type有三个取值——stop、ignore、exclude作用是塑造时钟提取的边界# 让某个分支不再往平衡范围里走 set_ccopt_property sink_type -pin [get_pins u_core/u_clkdiv/clk_out] stop # 把一段纯逻辑挡在时钟网络之外 set_ccopt_property sink_type -pin [get_pins u_dsp/u_gate/en] ignorestop用于时钟传播到某个点就截止的场景比如分频器输出ignore用于明确不属于时钟平衡对象、但结构上又连着时钟的引脚exclude则是把对象整体排除。三个值用错表现出来都是时钟树莫名其妙断成两截或某个分支被漏掉所以设完一定要配合后面的调试图确认。3.7 提取之后的细化配置上面这些是全局设置。时钟网络提取完成之后还可以按更细的粒度覆盖修改自动生成的 clock 和 skew group、增加或删除 skew group、对单个 clock 或 skew group 打属性、改掉从 SDC 提取来的过渡和偏斜目标全局目标缺失时工具会退回用 SDC 推导的值。粒度上支持按 power domain、clock tree、skew group 分别指定。这也是前面强调先跑提取、再细化的原因——更细的设置只有在时钟网络提取出来之后才有对象可挂。4. 分阶段调试cluster、trial、full 三个断点停下来看什么4.1 九个阶段与每个阶段的观察点时钟少、门控结构简单的设计用默认设置加第 3 章那些配置直接跑完就行。但时钟关系一复杂就值得在中间阶段停下来分析——哪怕最后跑完的时序和拥塞看起来都正常。中间结果里能看到的面积、运行时间、时序、功耗改进机会在最终结果里往往已经看不出来了。阶段动作停下来能看什么1应用 post-CTS 优化与时序设置OCV 模式、latency 是否被改2设置路由规则NDR 是否挂上、层级是否对3应用影响时钟提取的设置cell list 过滤结果、target 是否合理4从设计和约束中提取时钟skew group 与 clock tree 的划分是否符合预期5修改 clock 与 skew group增删是否生效、属性挂载位置6时钟构建cluster → trial → full结构是否成形、缓冲数量增长曲线7early global route 与 conditioning时钟网络布线资源是否够8Nanoroute 与 conditioning实际寄生与预估的偏差9数据通路优化时序收敛与功耗第 4 阶段是分水岭。提取出来的结构如果和你的理解不一致后面所有调试都是在错误前提上做。第 6 阶段拆成 cluster、trial、full 三步是因为这三步的成本差距极大cluster 只要几分钟trial 十几分钟full 可能一整夜。4.2 断点怎么下明确每个阶段要回答的问题批处理脚本里按阶段切开每个断点存一次数据库# 阶段 1-3设置完成后先存一份确认设置本身没错 saveDesign ./db/pre_cts.enc # 阶段 4只做时钟提取不做构建 # 提取完立刻存库后面所有调试都从这个库出发避免反复提取 saveDesign ./db/extracted.enc # 阶段 5-6构建按 cluster / trial / full 分次推进 # 具体阶段开关随版本略有差异用 ccopt_design -help 确认后再写死关键习惯是每次断点都saveDesign。提取这一步耗时不算短而且它是后面所有调试的共同起点反复重跑提取纯属浪费。注意cluster 和 trial 阶段中断之后CCOpt 运行无法从中断点重启。想让流程继续只能从更早的数据库重新开始。这个限制直接决定了你的断点策略——不要在 cluster 阶段设交互式断点然后指望改完参数接着跑。4.3 提取之后、构建之前能改什么提取完成到开始构建之间是干预成本最低的窗口。常见操作是增删 skew group、对某个 clock tree 单独改 target、把某些引脚的类型重新定义。改完再进构建比构建完回头改要省太多时间——full 阶段之后想动结构基本等于重跑。4.4 调试三件套基本检查、时钟树调试器、路径追踪基本检查放在最前面时钟路径延迟、树的平衡性、扇出比这些先扫一遍能排掉一大半低级问题。接着是 CCOpt 的时钟树调试器。它是图形界面的能把树的拓扑结构直接摊开瓶颈在哪、哪条分支延迟明显不均一眼就看出来。这类结构性问题用命令查报告效率低不止一个数量级。第三条是时钟路径追踪加跨视图交叉探测在版图上点一条时钟路径同步定位到原理图和时序报告里的对应对象。定位这条路径为什么被插了这么多 buffer这类问题时跨视图联动比来回切报告快得多。此外还有一套 reporting 命令用于输出时钟树和 skew group 的统计信息以及 CCOpt properties 用于从属性侧反查工具对这个设计做了哪些判断。属性视角在排查工具为什么这么分树时特别有用——分组逻辑一旦和你预期不符看属性比看结果快。4.5 架构级干预把不该进时钟网络的东西挡在外面调试到最后往往会发现需要主动干预时钟树的构建方式。这份笔记里列了一整套手段我按实际使用频率排阻断数据通路参与时钟网络。设计里总有一些逻辑从结构上连着时钟、但语义上不属于时钟平衡对象让它们进树只会白增缓冲。用sink_type挡掉是首选。故意制造偏斜deliberate skew。有些路径就是需要时钟到达时间错开这时候不能指望工具自动生成得显式指定。平衡互不相交的时钟树。两组时钟在结构上没有公共节点但时序上需要对齐这属于需要手工建组的场景。阻止公共 master 派生的 slave clock 被一起平衡。分频、倍频出来的时钟如果被当成一棵树平衡工具会尝试把本不该对齐的点对齐结果两头不讨好。处理带时钟 feedthrough 的 macro。时钟穿过 macro 内部再出来这类结构是最容易把树断成两截的地方sink_type在这里要设准。禁用某些时钟的综合以及防止过度缓冲。前者用于完全不希望工具碰的时钟后者通常和target_skew给得太紧、或者 cell list 给得太宽有关。提示这些架构级干预在大多数设计里其实用不上。先把基础设置调对确认结果确实受限于结构问题再上这些手段。上来就堆属性最后连是哪个属性起作用都说不清。5. 避坑与排查五条踩过的记录5.1 中断在 cluster 阶段改完参数发现跑不下去现象在 cluster 或 trial 阶段停下来改了几个属性想继续跑工具报错或者结果直接作废。原因CCOpt 在 cluster 和 trial 阶段中断之后不支持重启这个限制是流程本身决定的不是配置问题。解决断点只下在支持继续的位置。想在 cluster 阶段看中间结果就把该阶段的产物存成数据库后续从更早的数据库重新推进而不是指望原地续跑。复杂设计上我一般把完整重跑脚本化跑一遍的成本比手工修补低。5.2 时钟树上的缓冲器数量远超预期现象full 阶段跑完时钟树 buffer 面积占比明显高于同类项目。原因八成是 cell list 传了空列表。空列表在这个上下文里的语义是所有单元都纳入候选工具会把整个库的 buffer 都拿来试。其次是target_skew给得过紧触发过度平衡。解决先跑report_ccopt_cell_filtering_reasons看过滤结果确认候选集是不是失控再把target_skew放宽 20% 重跑对比缓冲数量。这两步做完多数情况下面积能回到合理区间。5.3 库里标了 dont_use 的单元还是被插进时钟树现象库文件里明确禁用的低阈值单元出现在时钟树上。原因18.1 之前的版本里CCOpt 的 cell 属性会覆盖库定义的 dont_use用户自己打的 dont touch 也是从 18.1 起才被尊重。解决把工具版本升到 18.1 以上同时确认 cell list 里没有把这些单元写进去。升级之后仍然出现就查是不是脚本里另一套命令体系又把列表覆盖了一遍——legacy 和 CUI 混写时这种覆盖不报错。5.4 IO 延迟在 CTS 之后被改动顶层时序对不上现象模块级 CTS 结果正常集成到顶层后 IO 时序全错。原因CTS 阶段默认会去修改 IO latency模块级单独看没问题一进顶层就暴露。解决在 post-CTS 设置里显式关闭set_ccopt_property update_io_latency falseCUI或set_db cts_update_clock_latency falselegacy。芯片级设计和时钟架构特别复杂的设计这一条应该是标配。5.5 时钟穿过 macro 之后树断成两截现象调试器里看到时钟网络在 macro 边界处分成两段两段各自平衡跨 macro 的时序怎么都收不干净。原因feedthrough 引脚被默认判定成了 sink时钟传播在那里停住后面的段被当成另一棵树处理。解决检查该 macro 时钟引脚的sink_type按实际意图改成ignore或调整取值让时钟穿过 macro 继续传播。改完必须重跑提取再构建只在构建阶段改没用。6. 装完怎么验证三条验收线和一个回归习惯环境装完、脚本跑通不等于这套环境可用。我验收跑三件事。第一条线结构验证。提取阶段结束时比对 skew group 数量、clock tree 数量与预期是否一致看 sink 数量有没有明显缺口。这一步在主流程里做几秒钟的事但能拦住后面百分之八十的返工# 提取完成后立即执行确认结构划分符合预期 saveDesign ./db/extracted.enc # 输出时钟树与 skew group 概览重点看数量与分组 # 报告命令按本地版本选择CUI 与 legacy 前缀不同第二条线时序与物理验证验收项看什么判断标准插入延迟各 clock tree 的 latency与上一版偏差在可解释范围内偏斜各 skew group 的 skew 统计不超过 target_skew 设定值过渡时钟网络 max transition不违反库 DRV且接近 target_max_trans布线规则时钟网是否走 NDRtop/trunk/leaf 三层规则均生效面积时钟树 buffer 面积占比与同类设计横向对比无异常第三条线可重复性回归。同一个数据库、同一份脚本跑两遍缓冲器数量差异应该在个位数百分比以内。差异大了说明有随机性或未固定因素——通常是 cell list 没锁定或者 cluster 阶段的分组受多核调度影响。# 两遍回归日志分开存最后 diff 关键统计 innovus -no_gui -files run_ccopt.tcl -log cts_regr_a.log innovus -no_gui -files run_ccopt.tcl -log cts_regr_b.log从那以后我每次跑完 full CTS不管时序看起来多干净都强制走一遍提取库的对比回归——两遍缓冲数量一对脚本里哪一行被改坏了立刻现形。血泪经验是CTS 的问题从来不会在 CTS 阶段自己暴露它总是等到顶层集成才翻出来那时候后悔药已经没有了。希望帮到你。本文还有配套的精品资源点击获取