资讯动态

CCOpt复杂时钟树调优:skew、sink_type与buffer爆炸排查

发布时间:2026/10/29 14:51:33 来源:尧图企业网站定制
简介这份英文技术文档面向集成电路物理实现工程师聚焦Cadence Innovus 18.1/19.1中CCOpt在复杂时钟网络配置与调试中的应用适合具备时钟树综合基础、需要处理高性能低功耗时钟树问题的设计人员。资源包仅含1个PDF文件大小约1.3MB便于随查随用。内容围绕运行准备、推荐配置及CCOpt主流程各阶段的可调试点展开并给出基本检查、CCOpt时钟树调试器、时钟路径追踪与跨视图探测、报告命令、利用属性洞察设计等调试手段后段还涉及架构时钟控制与sink修改包括阻断数据通路进入时钟网络、有意偏斜、平衡不相交时钟树、避免共同主时钟派生的从时钟被平衡、管理不应偏斜的复杂控制逻辑以及保持输出引脚驱动逻辑的相似偏斜等专题。目前已有211人学习可帮助读者建立从配置到定位问题的完整思路提升复杂时钟网络的调试效率。1. 时钟树越平衡越差CCOpt 跑完 hold 反而掉了 300ps 的反直觉现场用 cadence innovus 的 ctsCCOpt跑一个多时钟域设计很多人第一反应是把 skew 目标往死里收——150ps 不够改 50ps觉得越紧越安全。结果跑完一看时钟树上的 buffer 从 1.2k 涨到 3.4k面积顶上去功耗也跟着涨跨时钟域路径的 hold 违例反而多出几百条同一棵树的两个分支实测差得比改之前还离谱。这不是工具坏了是 CCOpt 在复杂时钟网络上被你的约束带偏了它认真地平衡了一堆本来就不该被平衡的分支把一条本该短的路径硬拉长余量自然被吃掉。这类故障在带分频器、多路选择时钟、macro 穿通时钟的设计里几乎必现尤其适合正在被 CTS 后时序、面积、功耗三条线同时拉扯的物理设计工程师——下面按报错现象→根因→怎么改一层层拆。2. 提取阶段就错了cell 列表、target、sink_type 三处没设对后面全白干CCOpt 能自己从 library、design 和 SDC 里推断出大部分配置问题是这些数据源里的值本来就是给 signoff 用的不是给造时钟树用的。最常见的翻车不是命令写错而是什么都没写让工具自己猜猜出来的结果在 report 里看着正常物理实现完全是另一回事。2.1 cell 列表留空不是不限制是全都要先看一个特别反直觉的坑。有人图省事这么写# 错误示范以为空列表等于不限制 cell 类型 set_ccopt_property buffer_cells {} set_ccopt_property inverter_cells {}跑完之后时钟树上出现一堆你没预期的低驱动、甚至不同阈值电压家族的 cell某些 corner 下 transition 直接压不住。原因写在手册里但很容易被跳过空列表有特殊语义CCOpt 会把它理解成不管 Vt 家族、不管驱动能力库里所有 cell 都可以挑而不是不做限制。这在多 Vt 库里是灾难工具很可能挑中一堆面积小但驱动弱的 cell然后在后面疯狂补 buffer 来救 transition。正确做法是把列表显式钉死而且 legacy 命令和 CUIStylus两套写法都要知道因为同一份脚本里经常混着两种风格# legacy 写法在 clock extraction 之前把可用 cell 定下来 set_ccopt_property buffer_cells {BUF_X2_LVT BUF_X4_LVT BUF_X8_LVT} set_ccopt_property inverter_cells {INV_X2_LVT INV_X4_LVT INV_X8_LVT} set_ccopt_property clock_gating_cells {CKGATE_X2_LVT CKGATE_X4_LVT} set_ccopt_property logic_cells {AND2_X2_LVT OR2_X2_LVT MUX2_X2_LVT} # CUI / Stylus 写法语义一一对应 set_db cts_buffer_cells {BUF_X2_LVT BUF_X4_LVT BUF_X8_LVT} set_db cts_inverter_cells {INV_X2_LVT INV_X4_LVT INV_X8_LVT} set_db cts_clock_gating_cells {CKGATE_X2_LVT CKGATE_X4_LVT} set_db cts_logic_cells {AND2_X2_LVT OR2_X2_LVT MUX2_X2_LVT}这里几个参数的取舍值得说清楚。clock_gating_cells和logic_cells一定要给不给的话 CCOpt 只能把 clock gating cell 和时钟路径上的组合逻辑当成固定障碍物绕开结果是本该被吸收进时钟树优化的门控单元全部保留buffer 数量下不来。buffer_cells和inverter_cells里放什么驱动档位直接决定树的形状只放 X2 和 X4工具为了压长线会级联很多级加上 X8、X12级数降下来但单级功耗上去。我一般按最长路径用大一档、普通分支用中档配两组再在抽取之后按 skew group 覆盖。还有一条版本相关的行为必须记住如果某个 cell 写进了 CCOpt 的 cell 属性它会覆盖 library 里定义的 dont_use但从 18.1 开始用户自己打的 dont touch 是会被尊重的不会被属性翻掉。以前那种我在属性里写了它就能用的经验在升级之后可能直接失效报出来的现象是某些 cell 明明在列表里却一个都没用上。怀疑列表被过滤别靠猜跑这两条# legacy / CUI 各一条输出每个 cell 被剔除的具体理由 report_ccopt_cell_filtering_reasons report_cts_cell_filtering_reasons输出里会写清楚是驱动不够、Vt 不匹配、被 dont_use 挡住还是被用户 dont touch 挡住。这一步能省掉大量为什么我配了却没用的来回。另外把实例设成 size-only 是个灵活度更高的做法工具可以在不换 cell 类型的前提下调驱动代价是有些 cell 最终根本不会被用到——这是正常的不是配置失败。2.2 target_skew 收太紧等于主动要求过度平衡第 1 章那个 hold 变差的现场根因基本都在这里。target_skew和target_max_trans是两个必须显式给的值不给的话 CCOpt 会从 SDC 里推断而 SDC 里的值往往是按最悲观 corner 写的拿来造树就过头了。# 显式给全局目标不要指望从 SDC 自动推 set_ccopt_property target_max_trans 0.12 set_ccopt_property target_skew 0.08 # CUI 等价写法 set_db cts_target_max_transition_time 0.12 set_db cts_target_skew 0.08target_max_trans在低功耗设计里权重很高它直接决定时钟网络的总电容也就决定了动态功耗。但注意 CCOpt 本身会规避 library 里定义的 DRV所以这个值不是越小越好收到工艺根本做不到的时候工具会用更多级 buffer 硬凑面积和功耗双输。target_skew同理收得过紧首先影响的是 clustering 阶段——它会把本该分开处理的 sink 强行聚到一起然后整棵树为了那几十 ps 付出了成倍的 buffer。这两个值在抽取之后还能按clock_tree、skew_group、power_domain三个粒度覆盖这是复杂设计里最有用的一招全局给一个宽松值兜底只对真正敏感的跨域组收紧。# 抽取完成后只对跨域那个组收紧其余保持宽松 set_ccopt_property -skew_group sg_cross_domain target_skew 0.05 set_ccopt_property -skew_group sg_cross_domain buffer_cells {BUF_X4_LVT BUF_X8_LVT}参数含义上要留意全局target_skew作用于所有 skew group组级设置优先级更高而组级buffer_cells会替换掉全局列表而不是追加。如果你在组级只写了两个 cell工具就只在这两个里挑驱动档位不够的话会看到该组 buffer 级数明显多于其他组。2.3 sink_type 不设时钟网络提取会把数据端也画进树里这是复杂时钟网络里最隐蔽的一类问题抽取出来的时钟树比你想的大一圈report 里 sink 数量对不上某些本不该参与平衡的引脚出现在时钟网络上。根因是把不该继续传播的点当成了普通 sink工具顺着往下找把下游的组合逻辑、甚至数据路径的一部分当成了时钟负载。sink_type就是用来裁剪提取范围的三个取值语义差别很大搞混了后果完全不同取值作用典型用途stop该点作为时钟网络终点下游不再向下提取分频器输出、门控单元输出侧、macro 时钟输入脚ignore该点不作为平衡目标但仍留在网络里测试逻辑、扫描链上不影响功能的时钟脚exclude从时钟网络提取中整体排除完全不归 CCOpt 管的旁路时钟写法上legacy 和 CUI 都用set_ccopt_propertypin 级设置的关键是把 pin 路径写全# 让分频器的输出成为时钟树终点下游由它自己重新起树 set_ccopt_property sink_type stop -pin u_core/u_clkdiv/CKOUT # 扫描测试用的旁路时钟脚不作为平衡目标 set_ccopt_property sink_type ignore -pin u_dft/u_bypass/CK要注意的是不同版本里set_ccopt_property的参数顺序有过调整写之前用help set_ccopt_property核一下顺序比照抄别人的脚本靠谱。配完立刻验证不要等到跑完整个 flow 才发现没生效# 抽取出时钟网络后先确认 stop 点真的停了 report_ccopt_clock_trees report_ccopt_skew_groupsreport_ccopt_clock_trees看的是结构每个 clock tree 下挂了多少 sink、从哪个根起、经过哪些层次report_ccopt_skew_groups看的是平衡关系哪个组属于哪棵树、目标 skew 是多少。正常的设计里 skew group 和 clock tree 基本是一对一一旦你看到某个树下挂了三四个组或者 sink 数量比你手数的多出一截就是 2.3 这节没做干净先回去补 sink_type别急着调 skew。3. 平衡不动与 buffer 爆炸skew group 和 clock tree 对不上时怎么拆这一章对应的是那种跑得完但结果不对的故障。CCOpt 不报 errorccopt_design正常结束但 report 里某个组的 skew 怎么都收不下去或者一棵树上插了几百个 buffer 还是不平衡。这时候问题通常不在物理而在你对时钟结构的描述和工具理解的结构不一致。3.1 skew group 和 clock tree 不是一对一平衡就落到了错误的层先明确两个术语的区别这是后面所有排查的基础。skew group 是时钟网络的结构性组成部分CCOpt 用它来建结构、做初始平衡clock tree 是用于平衡的时钟网络子集。绝大多数设计里两者一对一复杂结构里会出现一个 clock tree 下挂多个 skew group。多对一的后果是平衡发生在 clock tree 这一层你在 skew group 上设的target_skew只影响该组内部跨组的相对关系由上层决定。所以现象是——你在sg_a和sg_b上都设了 50ps 目标两个组内部都做到了 40ps但组间差了 400ps跨组路径照样违例。诊断顺序我一般这么走# 第一步看清楚有几棵树、几个组、谁挂在谁下面 report_ccopt_clock_trees report_ccopt_skew_groups # 第二步确认目标值到底生效在哪一层 report_ccopt_skew_groups -skew_group sg_a输出的组信息里会列出该组的成员 sink、目标 skew、实际 skew 以及它归属的 clock tree。如果实际 skew 明显小于目标但组间差异很大基本可以确认是层级问题继续调组级参数没用得动结构。处理方式取决于你的意图。如果两组本来就该一起平衡那正确做法是把它们合并或让它们共用一棵树而不是各自设目标如果两组物理上离得很远、本来就不该一起平衡那就必须拆开——delete_skew_group删掉自动生成的组再用create_skew_group手工建并把不该共享的分支用sink_type stop切断。手册里提到的 exclusive skew group creation 就是干这个的把不该互相牵扯的组建成互斥组让平衡各自独立进行。这里有个容易忽略的副作用拆组之后每棵树都变短了buffer 总数通常会下降但跨组路径的 skew 完全依赖约束保证。拆之前一定要确认 SDC 里对跨组路径有明确的set_clock_groups或set_max_delay兜住否则你只是把违例从一面墙挪到了另一面墙。3.2 从同一个 master 派生的 slave clock 被强行平衡这是复杂时钟网络里最典型的工具太勤奋案例。设计里一个 master clock 经过分频、门控派生出几个 slave clock功能上它们之间是异步或者有确定相位关系的但 CCOpt 看到它们共享上游路径就顺手把下游也一起平衡了。现象很好认某条 slave clock 的树被插了一堆 buffer插得毫无道理因为这条 clock 的 sink 本来就少、物理距离也近。查下去会发现这些 buffer 是为了把它的延迟拉到和另一条 slave clock 一致——而那条 clock 你根本没打算跟它对齐。解决路径有两条选哪条看你的设计意图。第一条如果 slave clock 之间确实不需要对齐就在 master 分叉点直接切断# 在 master 分出 slave 的那个点设 stop让下游各自起树 set_ccopt_property sink_type stop -pin u_core/u_ctrl/u_div/CLKOUT # 切断后重新抽取确认两条 clock 变成两棵独立的树 report_ccopt_clock_trees第二条如果它们需要保持确定关系但不能被平均化就给它们建独立的 skew group各自设目标把相对关系交给约束去描述# 给 slave clock 单独建组目标按各自的实际需求给 create_skew_group -name sg_slave_fast set_ccopt_property -skew_group sg_slave_fast target_skew 0.06参数上要注意create_skew_group之后必须重新跑一次抽取组才会挂上 sink直接在旧结果上设属性是无效的report 里会显示组存在但成员为空。这个组建了但没成员的状态很坑因为不报错。3.3 复杂控制逻辑不该被 skew用 architectural clock control 关掉时钟路径上挂着控制逻辑——多路选择器、门控、模式切换——这些门后面的分支功能上可能永远不会同时活跃。工具不知道这件事它看到两个分支就想去平衡结果是把一条本来很短的路径硬拉长只为了跟一条在另一个模式下才用的路径对齐。判断方法看report_ccopt_skew_groups里那些 sink 数量不多、但实际 skew 和目标差很远的组再看这些组的 sink 是不是分布在不同功能模式下。如果是用sink_type配合组划分把它们分开# 把只在测试模式下使用的分支排除在平衡之外 set_ccopt_property sink_type ignore -pin u_core/u_test_mux/Z # 需要完全交给外部时钟树处理的分支直接排除 set_ccopt_property sink_type exclude -pin u_core/u_ext_clk/CKignore和exclude的区别在实际效果上很实在ignore的引脚还留在时钟网络里会被计入 transition 和电容的统计只是不作为平衡目标exclude的引脚直接从网络里消失连负载都不算。如果这个引脚实际上是有电容的用exclude会让你的延迟估算偏乐观signoff 时对不上所以我一般优先用ignore只有确实不归 CCOpt 管的旁路时钟才用exclude。另外一类不该被 skew 的是驱动输出引脚的那部分逻辑。芯片对外输出的时钟通常要求它的 skew 和某个内部时钟保持相似而不是和它所在的整棵树保持一致。这种场景下把那部分逻辑单独划组、设一个宽松目标比让它跟着大部队平衡要稳得多。4. macro 穿通与输出引脚跨模块的时钟路径怎么把平衡带偏前面几章处理的是单模块内的结构问题这一章处理跨边界的情况。带时钟穿通的 macro、驱动输出引脚的逻辑、需要完全禁掉综合的 clock这三类东西是复杂时钟网络里最容易产生延迟模型和实际对不上的地方。4.1 macro 内部穿通时钟两侧被当成一条连续网络有些 macro 的时钟从一侧输入、从另一侧输出中间在 macro 内部过了一段缓冲。对 CCOpt 来说如果没有额外信息它会把 macro 输入和输出当成同一条网络上的两个点延迟估算只算到 macro 的输入脚穿通那一段完全没算进去。结果是macro 后面的那部分树实际延迟比工具以为的长一截skew 报告看着是平的实测差了一大截。这类问题在只跑 CTS 不跑 STA 的中间阶段特别难发现因为 report 里一切都是对的。处理方式是两头都做标记——输入侧当终点输出侧当新起点# 输入侧时钟网络在此终止穿通段不由 CCOpt 负责 set_ccopt_property sink_type stop -pin u_macro/u_clkbuf/CKIN # 输出侧从这里重新起一棵树目标按 macro 后的实际需求给 set_ccopt_property sink_type stop -pin u_macro/u_clkbuf/CKOUT关键在于输出侧也要设 stop 并在抽取后确认它变成了一棵新树的根。如果只设了输入侧工具会认为网络在 macro 内部继续输出侧仍然挂在原来那棵树上穿通延迟照样不算。验证的时候直接看树的结构根节点应该出现在 macro 输出脚上# 确认 macro 输出成了独立的树根 report_ccopt_clock_trees如果你的库里给这个 macro 提供了带延迟的时序模型那还有一条更省事的路让工具用模型里的延迟不去穿通追踪。但这条依赖库的质量模型不准的时候反而更糟我一般还是手工切断行为可控。4.2 输出引脚驱动的逻辑要单独保 skew芯片输出时钟的常见要求是和某个内部寄存器的时钟保持相似 skew而不是和整棵树平衡。这两件事听起来差不多实际差很远跟着整棵树平衡你会得到一棵延迟很低但和内部时钟完全不对齐的树。做法是把驱动输出引脚的那几个寄存器单独划组目标值按内部参考时钟的实际延迟来定而不是按全局默认值# 给输出引脚驱动逻辑单独建组目标按参考时钟的实测延迟给 create_skew_group -name sg_clkout_drv set_ccopt_property -skew_group sg_clkout_drv target_skew 0.03这里target_skew的取值要看内部参考时钟那条路径的插入延迟。常见做法是先把主树跑完、从 report 里读出参考组的实际 insertion delay再回来给这个组设目标两轮迭代就能对上。一轮就想到位的可能性不大除非你对这棵树的延迟分布非常熟。还有一个副作用要提前防给输出组单独设了很小的target_skew之后工具会优先保证这个小目标代价可能是主树多插 buffer。如果你发现主树的 buffer 数在你加了输出组之后明显上涨就是这个原因。这时候把输出组的目标适当放宽或者给它单独的 cell 列表限制驱动档位把代价圈在这一小块里。4.3 某些 clock 根本不该被综合禁掉比调参省事有些时钟在设计里是外部提供的或者已经用别的方式手工做好了树CCOpt 再插一手只会把已有的结构打乱。这类 clock 最省事的处理是整体禁掉综合而不是一个个调参数。判断标准很简单这条 clock 的树在跑 CCOpt 之前就已经满足要求或者它的 sink 全部在 macro 内部不归你管。禁掉之后要注意它对时序分析的影响——树还在只是不由 CCOpt 建所以延迟得靠约束或者实际布线来保证。# 把整条 clock 上的 sink 标记为不参与平衡 set_ccopt_property sink_type ignore -pin u_analog/u_clkin/CK # 禁完之后必须重新抽取否则旧结构还在 # 抽取后确认这条 clock 下没有新增 buffer report_ccopt_clock_treesignore和前面说的exclude在这里的选择很关键ignore保留负载统计延迟估算不会偏exclude会让这条 clock 从网络里完全消失如果你后面还要基于这个网络做时序分析数据就没了。除非确认这条 clock 后续完全不碰否则用ignore。另一类需要防的是过度插 buffer。判断是不是过度有个简单的量化标准看时钟网络上的 buffer 总数和 sink 总数的比值。经验上这个比值超过某个阈值跟工艺和频率有关得看你自己的基线就说明树被插胖了常见原因是target_max_trans收太紧、cell 列表里缺大驱动档位、或者上面说的过度平衡。三者的区分方法是看report_ccopt_cell_filtering_reasons的输出如果大驱动 cell 被大量过滤掉是列表问题如果列表没问题但级数还是多是 trans 目标问题两者都正常但组间 skew 差异大就是平衡目标问题。5. 避坑清单CCOpt 调试里最容易翻车的五个动作5.1 在 trial 阶段停下看结果想改参数接着跑现象跑到 trial 阶段觉得结果不对停下来改了属性想继续跑工具直接拒绝整个 flow 得从头再来。原因cluster 和 trial 阶段不支持停止后重启这是流程本身的限制不是配置问题。中途停下的状态没法恢复到可继续的上下文。解决所有需要看中间结果再改参数的调试一律在 extraction 之后、clock construction 之前停。这个点停下来是安全的改完属性能重新跑 construction。真要在 trial 阶段看数据就把那次运行当一次性的诊断跑看完丢掉改完从抽取重新开始。5.2 SDC 里的值和全局属性打架你设的那个没生效现象明明设了target_skew 0.08report 里显示的目标却是 0.15或者组级设置完全没起作用。原因优先级记反了。SDC 派生出来的 transition/skew 目标只在没有全局目标的时候才被使用一旦你设了全局值SDC 派生值就被压住。而组级设置又在抽取之后才能生效抽取之前的组级设置会被覆盖掉。解决固定一个顺序——先设全局跑抽取再设组级然后 construction。每次改完用report_ccopt_skew_groups确认组上的实际目标值别假设它等于你写的值。这一步花三十秒能省掉一整轮重跑。5.3 cell 列表和 dont_use / dont touch 的冲突没查现象列表里明明写了某个 buffer跑完一个都没用上或者反过来一个你标了 dont touch 的 cell 出现在了时钟树上。原因CCOpt 属性里的 cell 列表会覆盖 library 定义的 dont_use但 18.1 之后用户自己打的 dont touch 会被尊重。两套规则叠加行为在不同版本上不一致。解决升级版本之后第一件事就是跑report_ccopt_cell_filtering_reasons看每个 cell 被剔除的理由。别依赖以前这么写能用的经验。5.4 OCV 和延迟计算模式没配对debug 出来的 skew 结论全错现象CTS 阶段 report 出来的 skew 很漂亮signoff 一看差得远。原因调试时用的延迟计算模式和 signoff 不一致片上variation 没开或者 IO latency 的更新开关状态和设计不匹配。解决调试用的设置必须和 signoff 对齐这是前提不是可选项。# chip level 或架构特别复杂的设计先关掉 IO latency 自动更新 set_ccopt_property update_io_latency false set_db cts_update_clock_latency false # 打开片上 variation和 signoff 保持一致 setAnalysisMode -analysisType onChipVariation set_db timing_analysis_type ocvupdate_io_latency false这条在 chip level 尤其重要不关的话 IO 侧的延迟会被反复修正你在中间阶段看到的 skew 和最终结果对不上。关掉之后 skew 的绝对值会变但趋势是对的这才是调试需要的东西。5.5 布线规则只在全局设了没按 net_type 分开现象时钟主干线宽够了但叶子节点还是默认规则或者反过来全树都用了最激进的规则DRC 一堆。原因时钟网络的路由规则是按top / trunk / leaf三类分别生效的只设一个全局规则等于没区分。解决建好规则之后显式绑到三类网络上create_route_type -name clk_ndr_1 set_ccopt_property route_type -net_type {top trunk leaf} clk_ndr_1参数上-net_type支持同时列多个也可以分三次分别绑不同的规则——顶层用宽线、叶子用窄线是很常见的组合。绑完在 route 之前确认规则真的挂上去了别等到出 DRC 才回头查。6. 快速定位把时钟网络这只黑匣子按顺序打开真出问题的时候最忌讳的是上来就翻命令参考一条条试。我一般固定走四步顺序不换。第一步看结构report_ccopt_clock_trees确认树的棵数、每棵树的根、sink 数量。这一步回答的是工具理解的设计和我理解的一样吗不一样就回去补sink_type别往下走。第二步看平衡report_ccopt_skew_groups重点看三列组的成员数、目标 skew、实际 skew。实际明显小于目标的组说明它被上层树限制住了问题在结构不在参数。第三步看 cellreport_ccopt_cell_filtering_reasons确认你配的驱动档位没有被大面积过滤。这一步经常能发现以为配了其实没用的情况。第四步才进图形界面用 CCOpt clock tree debugger 沿着时钟路径 trace同时和布局视图交叉对照。结构和数字都对、但物理上明显不合理的时候才需要这一步因为前面三步能定位的问题占绝大多数。症状和排查入口的对应关系我整理成这样现象先看哪个报告关键线索skew 收敛不了report_ccopt_skew_groups目标与实际差值大且组属于同一棵树buffer 数量异常多report_ccopt_cell_filtering_reasons大驱动 cell 被过滤或 trans 目标过紧时钟网络比预期大report_ccopt_clock_treessink 数量对不上缺 sink_type 设置实测延迟比报告长结构报告 tracemacro 穿通段没被切断组设了参数没生效report_ccopt_skew_groups组存在但成员为空抽取没重跑拿这套顺序去跑多数情况下十分钟内能锁定是哪一类问题。真正花时间的从来不是改参数而是搞清到底哪里不对——所以顺序比技巧重要。最后说个我自己的习惯。刚上手那阵子我图快直接改 skew 目标看结果跑一轮二十分钟改三轮一下午就没了最后发现根因是最开始那个 cell 列表留空。从那以后我每次跑 CCOpt不管设计多简单前面那组配置——cell 列表、trans 目标、skew 目标、sink_type——都强制走一遍跑完先看三个 report 再谈时序。这份 CCOpt 配置与调试手册把 flow 各阶段能 debug 什么、architectural clock control 的几种典型场景、以及每个阶段该看哪个报告串得很清楚遇到本文没覆盖到的分支对着目录翻到对应章节比在命令参考里瞎翻快得多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑