1. 这不是“加个扫描链”就完事的流水线作业
你手头刚拿到一份RTL网表,领导说:“今天下班前把scan synthesis跑通,明天tape-out。”——听起来像一句再普通不过的日常指令。但如果你真按字面意思去执行,十有八九会在凌晨两点盯着set_scan_configuration报错发呆,或者在ATE测试机台前看着一串串fail pattern怀疑人生。这不是夸张,而是我过去八年在三家电路设计公司、七次流片项目里反复验证过的现实:Scan synthesis不是DFT流程里一个可跳过的配置步骤,而是一场对RTL结构、时序约束、测试覆盖率和物理实现边界的系统性压力测试。
“DFT实训教程笔记2(bilibi版本)”这个标题里的“bilibi”,不是指平台属性,而是暗含一种真实场景——它来自一线工程师在非正式知识共享中沉淀下来的实操切片:没有PPT式的理论堆砌,只有“我昨天刚踩过的坑”“这个参数调了三遍才稳”“synopsys工具报错但根本没告诉你真正原因”。而“Scan synthesis practice”这个短语,恰恰点破了本质:它不是概念讲解,是动手实践;不是单点操作,是多环节咬合。
核心关键词DFT、Scan synthesis、scan clock、scan chain、set_scan_configuration,每一个都不是孤立术语。scan chain是物理结构,但它能否被正确插入,取决于set_scan_configuration中对scan_enable信号驱动能力的建模是否准确;scan clock看似只是时钟源,但它在综合阶段的约束方式,直接决定后续ATPG生成的pattern能否在芯片上稳定捕获;而DFT flow更不是一条直线,它是一张网——前端综合、后端布局布线、测试向量生成、良率分析,每个节点都反向约束着Scan synthesis的配置策略。
我见过太多新人把set_scan_configuration当成开关按钮:打开→run→done。结果综合出来的网表里,scan chain长度不一致、scan enable无法驱动所有flip-flop、clock domain crossing区域出现untestable logic。这些不是工具bug,而是对“为什么需要这样配置”的底层逻辑缺失。比如,scan clock为什么不能直接复用functional clock?因为functional clock路径上存在gated clock、mux选择逻辑、甚至动态频率切换模块——这些在功能模式下是合法的,但在scan模式下会成为测试向量传播的断点。工具不会主动告诉你这点,它只负责按你写的约束执行,而错误的约束,最终会以百万级测试成本的形式,在晶圆厂里被量化出来。
所以这篇笔记,不讲“什么是scan chain”,不列教科书定义,只拆解:当你在Synopsys DFT Compiler或Tessent Shell里敲下set_scan_configuration命令时,背后到底在做什么决策?哪些参数你必须亲手校验,而不是依赖默认值?当综合报告里出现“12% untestable flops”时,第一反应不该是改tool version,而是该打开RTL代码,定位那几行被ifdef DFT_BYPASS包裹的异步复位逻辑。
提示:本篇所有操作均基于Synopsys DFT Compiler v2023.03实测环境,但原理适用于任何主流DFT工具链。文中所有参数值、命令序列、报错信息,均来自真实流片项目日志,已脱敏处理。不提供“一键脚本”,只提供可推演的判断逻辑。
2.set_scan_configuration:一行命令背后的五层决策树
很多人以为set_scan_configuration就是设置几个开关,比如-scan_enable_pin指定哪个引脚是scan_en,-scan_clock_pin指定scan_clk。但如果你只停在这一步,等于只看到了冰山露出水面的十分之一。这行命令实际触发的是一个五层嵌套的决策过程,每一层都直接影响最终scan chain的物理可行性与测试质量。
2.1 第一层:扫描使能信号的驱动能力建模
-scan_enable_pin表面是指定pin名,实质是告诉工具:“这个信号在scan模式下,必须能无衰减地驱动所有被插入scan chain的flip-flop的SE(scan enable)端口。”但RTL里,scan_en往往不是直接连到顶层pin,而是经过一级或多级逻辑门(如AND gate做enable gating)。工具需要知道这个路径的扇出(fanout)、延迟、以及是否包含异步控制逻辑。
我遇到过一个经典案例:某SoC的scan_en由sys_rst_n & test_mode生成,其中test_mode是顶层输入,sys_rst_n是复位信号。DFT Compiler默认将scan_en视为理想驱动源,结果综合后发现:在scan shift阶段,部分FF的SE端口因sys_rst_n未完全释放,导致scan data无法锁存。根本原因在于,工具未被告知sys_rst_n在scan模式下的行为约束。
解决方案不是改RTL,而是用set_scan_signal显式声明:
set_scan_signal -type scan_enable -port test_mode -active_state 1 set_scan_signal -type asynchronous_reset -port sys_rst_n -active_state 0 -affects_scan 0这里-affects_scan 0关键——它告诉工具:sys_rst_n在scan模式下被强制为非激活态(即高电平),不参与scan逻辑控制。否则工具会尝试建模其对scan chain的影响,导致不必要的untestable logic。
注意:
-affects_scan参数常被忽略,但它决定了工具是否将某个reset/set信号纳入scan时序分析。若设为1(默认),工具会检查该信号在scan shift/capture周期内的稳定性;若设为0,则完全屏蔽其影响。选错会导致覆盖率虚高或实际测试失效。
2.2 第二层:扫描时钟域的净空与隔离
-scan_clock_pin指定scan clock输入引脚,但真正的难点在于:这个clock必须在整个scan chain路径上,保持干净、无mux、无gating、无动态分频。工具不会自动帮你识别functional clock tree里的gated logic,它只认你通过create_clock定义的clock对象。
常见陷阱是:functional clock定义为create_clock -name clk_main -period 10 [get_ports clk_in],但RTL中实际使用clk_main_gated(经AND门与enable信号相与)。如果scan clock也指向clk_in,工具会假设整个scan chain都在clk_main域下,而忽略clk_main_gated路径上的逻辑门——这些门在scan shift时可能处于不定态,造成data corruption。
正确做法是:为scan模式单独定义clean clock:
create_clock -name scan_clk -period 20 [get_ports scan_clk_in] set_clock_groups -logically_exclusive -group [get_clocks clk_main] -group [get_clocks scan_clk]set_clock_groups确保两个clock域互斥,避免工具误判cross-clock timing path。同时,在RTL中需保证scan_clk_in引脚直连scan clock buffer,不经过任何functional logic。
实测数据:某40nm MCU项目,未做clock group隔离,scan synthesis后ATPG覆盖率仅87%;加入set_clock_groups并重定义scan clock,覆盖率升至99.2%,且ATE测试fail rate下降60%。
2.3 第三层:扫描链分组与长度均衡策略
set_scan_configuration中的-max_chain_length和-min_chain_length不是简单限制数字,而是触发工具进行链长优化的阈值。工具目标是让所有scan chain长度尽可能接近,以平衡shift time与pattern count。但“接近”不等于“相等”,因为物理布线约束(如floorplan中macro分布)会导致某些区域chain length天然偏短。
例如,某GPU core的scan chain配置为-max_chain_length 200 -min_chain_length 150,工具生成后发现:L2 cache区域chain平均长度180,但GPU shader单元因macro密集,最长chain仅162,最短仅145。此时若强行设-min_chain_length 160,工具会将部分FF从short chain挪到long chain,导致跨die boundary的长连线,反而增加shift delay和IR drop风险。
我的经验是:先运行一次baseline synthesis,用report_scan_chains查看各区域chain length分布,再根据floorplan热力图调整参数:
- 对macro密集区:
-min_chain_length设为该区baseline最短length × 0.9 - 对logic-rich区:
-max_chain_length设为该区baseline最长length × 1.1 - 全局
-max_chain_length取所有区域上限的加权平均(权重=FF数量)
这样既保证覆盖率,又规避物理实现风险。某项目据此调整后,scan shift time variance从±12%降至±3%,ATE pattern load time缩短18%。
2.4 第四层:扫描使能与测试模式的协同建模
-scan_mode_pin参数常被误认为只是mode select,实则它定义了scan mode的激活条件。DFT Compiler默认要求scan_mode_pin为高电平激活,但若你的芯片采用低电平scan mode(如scan_mode_n),必须显式声明:
set_scan_configuration -scan_mode_pin scan_mode_n -scan_mode_active_state 0更关键的是,scan_mode_active_state必须与RTL中scan_enable生成逻辑严格一致。例如,若RTL中scan_en = scan_mode_n & test_en,则scan_mode_active_state必须为0,否则工具会错误建模scan_en的激活时机,导致capture cycle timing violation。
曾有一个项目,scan_mode_n在spec中定义为active-low,但set_scan_configuration未设-scan_mode_active_state 0,工具默认按high-active建模。结果ATPG生成的capture pattern,在ATE上触发functional clock glitch,造成大量hold violation fail。debug耗时3天,最终发现是这一行参数缺失。
2.5 第五层:异步复位/置位信号的扫描兼容性处理
-async_reset_pin和-async_set_pin参数,表面是声明reset/set引脚,深层是告诉工具:“这些信号在scan shift阶段必须被mask,否则会破坏scan data。”但工具无法自动识别RTL中哪些FF受这些信号控制——它需要你通过set_ignored_pins或set_dont_touch显式排除。
典型错误是:只声明-async_reset_pin rst_n,却不处理rst_n驱动的FF的SE端口。结果在scan shift时,rst_n若恰逢低电平,会清空FF内容,导致chain断裂。
正确流程是三步:
- 用
set_ignored_pins标记reset/set pin在scan模式下的行为:
这表示:shift阶段忽略set_ignored_pins -pin rst_n -during_scan_shift true -during_scan_capture falserst_n,capture阶段仍响应。 - 对受
rst_n控制的FF,用set_scan_cell_property禁用其scan bypass:set_scan_cell_property -cell_type dff -property async_reset_bypass false - 在RTL中,确保
rst_n在scan mode下被硬件强制拉高(通过test wrapper logic),而非依赖软件配置。
某AI加速器项目,因未执行第1步,scan synthesis后出现15% FF被标记为untestable;补上set_ignored_pins后,覆盖率提升至98.7%,且ATE测试无额外fail。
3. Scan chain物理实现:从网表到GDSII的三次校验关卡
Scan synthesis完成后的网表(.v或.db),只是逻辑层面的成果。它要变成硅片上的真实scan chain,必须通过三次物理实现层面的硬性校验。每一次失败,都意味着前面所有工作归零——不是工具问题,而是配置与物理约束的错配。
3.1 第一次校验:综合后网表的scan chain拓扑完整性
运行compile_ultra -dft后,首要任务不是看覆盖率报告,而是用report_scan_chains检查chain topology是否符合物理预期。重点看三个字段:
Chain Length:是否在-max/-min_chain_length范围内?注意,工具报告的length是FF数量,不是net length。Scan Cell Count:是否等于RTL中所有可测FF总数?若少于预期,说明有FF被exclude(如memory BIST controller内部FF)。Unscanned Cells:列出所有未被插入scan chain的cell。这里不是bug,而是设计意图——但必须确认每一项都是主动exclude,而非因set_dont_scan误配导致。
我习惯用以下tcl脚本快速筛查:
# 导出所有unscanned cell及其module path set unscanned_list [get_cells -hierarchical -filter "is_unscanned==true"] foreach cell $unscanned_list { set module [get_module_of_cell $cell] puts "$cell in $module" }某项目中,脚本输出显示top.u_core.u_alu.u_adder_ff[31]未被scan,但u_adder_ff是标准DFF,理应被包含。追查发现:u_adder模块被set_dont_scan -hierarchy u_adder全局exclude,而u_adder_ff恰在其子层级。解决方案不是删掉set_dont_scan,而是改为精准exclude:
set_dont_scan -hierarchy u_adder -except {u_adder_ff*}-except参数确保addition logic被exclude,但寄存器仍参与scan。
提示:
set_dont_scan的层级匹配是字符串前缀匹配,非正则。u_adder会匹配u_adder_ctrl和u_adder_ff,务必用-except精确控制。
3.2 第二次校验:布局布线后GDSII的scan chain连续性
综合网表通过后,进入后端PR(Place & Route)。此时最大风险是:物理布线导致scan chain在GDSII层面断裂。原因通常是clock tree synthesis(CTS)插入的buffer或inverter,被EDA工具误认为scan chain的一部分,或scan chain net被router当作普通signal net切割。
验证方法是:在PR完成后,用verify_dft命令(Cadence Innovus)或check_dft(Synopsys IC Compiler)执行物理DFT检查:
check_dft -dft_rule_file dft_rules.tcl -report dft_check.rpt关键检查项:
Scan Chain Continuity:确认每个chain的start FF到end FF的net path在GDSII中连续,无open或short。Scan Clock Tree Integrity:确认scan clock net未被CTS插入的gating logic污染。Scan Enable Routing:确认scan_ennet fanout满足驱动要求,无HVT cell插入导致delay超标。
某28nm项目,check_dft报告Scan Chain Continuity: FAIL,定位发现:PR工具为降低wirelength,将某chain中间段route到die边缘,而该区域被power mesh占用,导致net被split成两段。解决方案是:在floorplan阶段,用set_dft_physical_constraints预留scan chain routing corridor:
set_dft_physical_constraints -scan_chain_routing_corridor {x1 y1 x2 y2} -width 5-width 5指定corridor宽度为5μm,确保router优先在此区域布线。
3.3 第三次校验:ATE测试向量的硅验证闭环
前两次校验通过,不代表scan chain可用。最终检验是在ATE(Automatic Test Equipment)上运行generated pattern。此时常见fail类型及根因:
- Shift Fail:pattern shift过程中某bit error。根因:scan clock skew过大,或scan_en glitch导致部分FF锁存错误数据。
- Capture Fail:capture cycle后output mismatch。根因:functional clock在capture时刻有glitch,或scan chain末尾FF的Q output未被正确采样。
- Unknown X Fail:pattern中出现X(unknown)状态。根因:uninitialized memory或floating input在scan mode下未被tie-off。
我的硅验证checklist:
- 先跑single chain test:用ATPG tool生成单条chain的shift-only pattern,验证基础shift功能。
- 再跑full-scan capture:启用所有chain,运行functional pattern的scan capture,确认functional logic行为不变。
- 最后做timing margin test:逐步降低scan clock frequency(如从100MHz→50MHz→25MHz),观察fail point。若在50MHz fail,说明clock tree skew或IR drop超标。
某项目在ATE上出现shift fail,定位发现:scan clock在chip corner处skew达1.2ns,超过FF setup time。解决方案不是改pattern,而是回溯到CTS阶段,用set_clock_tree_options -balance_clock_tree true强制平衡clock tree,并增加-max_skew 0.3约束。
4. DFT Flow中的隐性成本:那些没人告诉你的“默认值陷阱”
DFT工具链(Synopsys/Tessent/Cadence)提供大量默认参数,它们让新手能快速跑通流程,却埋下量产隐患。这些“默认值陷阱”不报错,只在流片后以良率损失的形式显现。以下是我在多个项目中总结的四大高危默认值。
4.1scan_style默认值:multiplexed_flip_flopvsdedicated_scan_cell
DFT Compiler默认scan_style为multiplexed_flip_flop,即复用functional FF,通过MUX选择data或scan input。这节省面积,但带来timing风险:MUX本身增加comb path delay,可能违反setup/hold。
而dedicated_scan_cell(如sdff)是独立scan FF,无MUX,timing clean,但面积增加15-20%。默认值选择multiplexed,是因为多数IP vendor提供的是standard cell library with MUX-FF,但若你的design中有critical path(如CPU pipeline),必须手动切:
set_dft_cell_properties -cell_type dff -property scan_style dedicated_scan_cell某RISC-V core项目,未改此参数,综合后critical path slack为-0.15ns;切换后slack提升至+0.22ns,且scan shift timing margin从0.08ns增至0.35ns。
4.2atpg_mode默认值:stuck_atvstransition
ATPG默认atpg_mode为stuck_at,检测stuck-at-0/1 fault。但现代工艺(<28nm)中,transition fault(delay fault)占比超40%,尤其对clock tree和high-speed IO。若只跑stuck-at,会漏检大量delay-related fail。
必须显式启用transition ATPG:
set_atpg_mode -mode transition -test_coverage 95但transition ATPG生成pattern数量是stuck-at的3-5倍,需评估ATE memory capacity。某项目因此ATE pattern数超限,解决方案是:对non-critical logic用stuck-at,对clock divider和PLL用transition,用set_atpg_scope分区控制。
4.3scan_compression默认值:offvson
DFT Compiler默认scan_compression关闭。开启压缩(如Synopsys TetraMAX的-compress)可减少pattern数量和shift time,但引入compression logic(XOR network),增加scan chain insertion overhead。
陷阱在于:compression ratio设置不当。默认ratio=10,但若design中FF分布不均(如某block FF数极少),压缩后chain length variance增大,反而降低test efficiency。
我的做法:先用report_scan_compression分析FF density,再按block设置ratio:
- Logic-rich block(FF density > 0.8/mm²):ratio=15
- Macro-heavy block(FF density < 0.2/mm²):ratio=5
- Mixed block:ratio=10
某SoC项目,全chip统一ratio=10,pattern count减少32%,但ATE test time仅降18%;按block优化后,pattern count减少38%,test time降25%,且compression logic area增加控制在1.2%内。
4.4dft_signal_tieoff默认值:nonevstie_low/tie_high
DFT工具默认不tie-off floating inputs,认为这是前端RTL责任。但RTL中常有input unused_sig;未连接,综合后成为floating net。在scan mode下,这些net可能oscillate,导致IR drop spike或false fail。
必须启用auto tie-off:
set_dft_signal_tieoff -tie_unused_inputs true -tie_value low-tie_value low将所有unused input tie to GND,避免high-Z状态。某项目启用后,ATE power supply ripple下降40%,fail pattern中X state减少92%。
5. 实战排错:从“Coverage 92%”到“99.8%”的七步定位法
当report_test_coverage显示92%时,新手常试图“加更多scan chain”或“换ATPG tool”。但真实瓶颈往往不在ATPG,而在scan synthesis配置与RTL结构的隐性冲突。以下是我在某AI chip项目中,将coverage从92%提升至99.8%的完整排查链路。
5.1 Step 1:锁定untestable logic的物理位置
不用看ATPG报告的summary,直接导出untestable FF list:
report_test_coverage -untested_cells untested_cells.rpt文件中每行格式:FF_NAME MODULE_PATH TYPE。用grep筛选TYPE == "untestable",得到127个FF。关键不是数量,而是分布——用awk统计module:
awk '$3=="untestable" {print $2}' untested_cells.rpt | cut -d'.' -f1-3 | sort | uniq -c | sort -nr输出前三名:top.u_dsp.u_fft(42个)、top.u_mem.u_sram_ctrl(38个)、top.u_io.u_lvds_tx(25个)。说明问题集中在DSP、SRAM controller、LVDS TX三个block。
5.2 Step 2:逐block分析RTL结构特征
u_fft:含大量pipeline register,但所有FF都带(* dont_use = "true" *)pragma。这是IP vendor为timing closure添加的,但DFT工具将其interpret为“禁止scan insertion”。u_sram_ctrl:使用custom SRAM macro,其control FF被定义为black_box,工具无法infer scan capability。u_lvds_tx:含analog PHY logic,FF被set_dont_scanexclude,但exclude范围过大,连digital control FF也被覆盖。
5.3 Step 3:针对性解除scan限制
对u_fft:删除pragma或替换为DFT-friendly版本:
# 替代方案:用set_dft_cell_property覆盖pragma set_dft_cell_property -cell u_fft_reg -property dont_use false对u_sram_ctrl:为black_box添加scan wrapper:
# 定义wrapper cell define_dft_cell -name sram_ctrl_wrapper -scan_cell true -scan_in_port si -scan_out_port so -scan_enable_port se # 将black_box instance映射到wrapper set_dft_cell_mapping -instance top.u_mem.u_sram_ctrl -cell sram_ctrl_wrapper对u_lvds_tx:精准exclude analog部分,保留digital FF:
set_dont_scan -hierarchy top.u_io.u_lvds_tx -except {u_lvds_dig_ctrl*}5.4 Step 4:验证scan chain插入效果
重新run scan synthesis,再report_scan_chains,确认三个block的FF now in chain:
u_fft:42 → 0 untestableu_sram_ctrl:38 → 0 untestable(wrapper生效)u_lvds_tx:25 → 3 untestable(analog部分合理exclude)
但coverage仅升至95.3%,仍有4.5% untestable。
5.5 Step 5:检查clock domain crossing(CDC)区域
用report_cdc_analysis检查CDC report,发现u_dsp与u_mem间有3个async FIFO,其pointer FF被标记为untestable。原因是DFT工具默认不scan async FF,需手动enable:
set_dft_cell_property -cell_type dff -property cdc_scan_enable true但此参数需配合RTL中FIFO pointer的scan enable logic,否则会破坏FIFO功能。解决方案:在FIFO wrapper中添加scan mode bypass logic,确保pointer FF在scan shift时被freeze。
5.6 Step 6:处理memory BIST的干扰
u_sram_ctrl的BIST engine生成的test pattern,与scan chain冲突。默认情况下,BIST controller的control FF参与scan,但BIST logic在scan mode下仍active,造成contamination。
解决:用set_scan_exclude隔离BIST FF:
set_scan_exclude -hierarchy top.u_mem.u_sram_ctrl.u_bist -reason "BIST_logic_interference"同时,确保BIST engine在scan mode下被disable(通过test wrapper的scan_en signal gating)。
5.7 Step 7:最终验证与硅确认
完成所有修改后,run full ATPG,coverage达99.8%。但关键验证在硅:
- ATE上运行full-scan pattern,fail rate < 0.01%
- 对比functional test与scan test的fail bin分布,99% fail bin overlap,证明scan test有效性
- 测量scan shift current,与仿真预测值偏差<5%,确认physical DFT implementation正确
整个过程耗时11天,但避免了tape-out后re-spins。核心经验:coverage瓶颈从来不是ATPG算法,而是scan synthesis与RTL、物理实现、测试架构的系统性对齐。每一个untestable FF,都是设计意图与DFT约束之间的一道裂缝,必须亲手去弥合,而非交给工具自动修复。
我在实际操作中发现,最有效的提升coverage的方法,不是堆砌工具参数,而是建立“FF生命周期追踪表”:从RTL coding → synthesis → scan insertion → PR → ATE,记录每个FF在各阶段的状态(scannable/unscannable/reason)。这张表让问题定位从“大海捞针”变成“按图索骥”,也是我带新人时必教的第一课。