
1. 从scanshift sdc模板这个标题说起它到底在解决什么问题第一次看到基于DFT的scanshift sdc模板这个标题很多人会愣一下——DFT、scanshift、sdc这三个词凑在一起指向的其实是数字集成电路可测性设计DFTDesign for Test里一个非常具体、也非常容易被忽视的环节扫描链scan chain在跨时钟域SDCSynopsys Design Constraints约束下的移位shift行为建模与约束模板化。先把三个词拆开说清楚避免后面越看越糊。DFT在这里不是离散傅里叶变换而是芯片设计流程里的Design for Test核心目标是在流片前把芯片内部的时序单元触发器、锁存器串成可控制的扫描链通过ATE机台灌入测试向量把制造缺陷筛出来。没有DFT芯片的测试覆盖率可能只有70%出头加上扫描链之后能拉到99%以上这是量产良率的生命线。scanshift指的是扫描链工作在移位模式下的那个阶段。芯片正常功能模式下触发器按功能时钟跑一旦切到scan shift模式所有扫描触发器被串成一条或多条长链用scan clock通常是慢速时钟把测试数据一位一位移进去、移出来。这个阶段对时序的要求和功能模式完全不同——它不关心逻辑深度只关心链上每一级触发器的setup/hold是否满足以及时钟树在移位模式下是否干净。sdc模板则是把上面这套移位模式的时序约束用一套可复用、可参数化的SDC脚本模板固化下来。为什么需要模板因为一个SoC里可能有几十条扫描链、十几个时钟域、多种shift频率配置如果每条链、每个corner都手写一遍约束既容易漏又没法维护。模板化的意义在于改一个参数比如链长、时钟名、频率整套约束自动适配。所以这个标题背后真正的问题是如何用一套结构化的SDC模板把scan shift模式下的时序约束表达清楚让DFT的移位操作在STA静态时序分析里能被正确检查同时不干扰功能模式的约束。适合读这篇内容的人大概有三类一是刚接手DFT约束、被shift模式时序违例搞得头大的后端/DFT工程师二是需要review SDC质量、判断约束是否完备的STA工程师三是想把DFT约束流程标准化、做成团队模板库的技术负责人。如果你属于其中任何一类下面的内容应该能帮你少走一些弯路。需要提前说明的是DFT约束这件事高度依赖具体工艺库、EDA工具版本和项目时钟架构我给的是基于常见工程实践的思路和模板骨架具体参数一定要结合自己项目的时钟定义和库文件来调不能照搬。2. scan shift模式的时序特征为什么它和功能模式约束是两套逻辑2.1 移位模式下时钟树的真实行为要理解scan shift的SDC为什么特殊得先搞清楚移位时时钟到底怎么跑。功能模式下时钟经过时钟门控、分频、多路选择路径复杂STA要检查的是从发射触发器到捕获触发器之间经过组合逻辑的最长/最短路径。而scan shift模式下扫描链上相邻两级触发器之间几乎没有组合逻辑——上一级的Q直接连到下一级的D或者只经过一个多路选择器的scan端。这意味着数据路径极短时序上通常不是问题真正的问题出在时钟路径上。移位模式下所有扫描触发器共用同一个scan clock或者少数几个shift clock这个时钟要同时驱动成千上万个触发器。时钟树在shift模式下的偏斜skew直接决定了移位能否正确。如果两条扫描链共用一个shift clock但时钟树分支延迟差异大就可能出现某条链的捕获沿比另一条晚导致数据错位。这就是为什么scan shift的SDC里时钟定义和时钟组clock group的设置比数据路径约束更关键。很多人写DFT约束时习惯性照抄功能模式的create_clock结果shift模式下时钟根本没定义对STA报出来的违例全是假的。2.2 shift与capture两个阶段的约束差异scan测试其实分两个阶段约束逻辑完全不同这是模板设计时最容易混淆的地方。Shift阶段scan enable拉高触发器串成链scan clock连续翻转数据一位位移入移出。这个阶段的特点是时钟频率通常较低比如10MHz到50MHz取决于ATE能力数据路径极短主要检查时钟偏斜和链上触发器的setup/hold。Capture阶段scan enable拉低芯片切回功能时钟在最后一个shift clock之后发射一个或两个capture clock把组合逻辑的响应捕获进扫描触发器。这个阶段的约束几乎等同于功能模式——要用功能时钟、要检查真实的组合逻辑路径。很多DFT约束出问题就是因为把这两个阶段混在一起写。正确的做法是shift阶段用独立的慢速时钟定义capture阶段复用功能时钟约束两者通过set_case_analysis或者模式切换来区分。下面这张表把两个阶段的核心差异列清楚维度Shift阶段Capture阶段时钟来源专用scan clockATE提供功能PLL/时钟时钟频率低速10-50MHz常见功能频率数据路径触发器直连极短完整组合逻辑主要检查时钟偏斜、链上setup/hold功能路径setup/holdscan enable恒为1恒为0约束复用独立模板复用功能SDC2.3 为什么必须用模板而不是手写一个中等规模SoC扫描链可能有50到200条时钟域5到15个PVT corner通常3到5个SS/TT/FF加上温度电压组合。如果每个组合都手写约束工作量是乘法级的而且极易出错。模板化的核心价值有三个参数化链长、时钟名、频率作为变量传入、一致性所有corner用同一套逻辑生成避免人为差异、可维护性时钟架构变了只改模板不用改几百行约束。我见过一个项目DFT约束是手写的结果FF corner下漏了一条链的时钟定义流片后测试程序跑不通返工排查花了两周。如果当时用模板这种漏定义在生成阶段就会被参数校验拦住。3. 模板骨架设计把scanshift约束拆成可复用的模块3.1 模板的输入参数该定义哪些设计模板的第一步是确定参数接口。参数定多了模板臃肿定少了不够灵活。基于常见项目我建议至少包含这几类参数时钟相关scan clock名称、周期、来源端口功能时钟名称列表扫描链相关链数量、每条链的触发器数量或最大链长、链的命名规则模式控制scan enable信号名、scan mode信号名、test mode端口工艺相关目标corner、库单元延迟等级频率配置shift频率、capture频率这些参数用Tcl变量在模板头部集中定义后面所有约束都引用变量而不是硬编码。这样换项目时只改头部主体逻辑不动。# 模板参数区 set SCAN_CLK_NAME scan_clk set SCAN_CLK_PERIOD 50.0 ;# ns, 对应20MHz set FUNC_CLK_LIST {clk_core clk_ddr clk_peri} set SCAN_ENABLE_PORT scan_en set SCAN_MODE_PORT test_mode set CHAIN_NUM 64 set MAX_CHAIN_LENGTH 1200 set SHIFT_FREQ_MHZ 20 set CAPTURE_FREQ_MHZ 800参数区放在最前面有个好处review约束的人一眼就能看到这个模板的关键配置不用翻到中间去找。3.2 时钟定义模块shift clock怎么建才干净shift clock的定义是模板里最需要小心的地方。常见做法是# shift模式专用时钟 create_clock -name $SCAN_CLK_NAME -period $SCAN_CLK_PERIOD \ [get_ports $SCAN_CLK_PORT] # 如果scan clock经过内部时钟树需要设置不确定性 set_clock_uncertainty -setup 0.5 [get_clocks $SCAN_CLK_NAME] set_clock_uncertainty -hold 0.3 [get_clocks $SCAN_CLK_NAME] # 时钟延迟ATE到芯片pad的路径 set_clock_latency -source 2.0 [get_clocks $SCAN_CLK_NAME] set_clock_latency 1.5 [get_clocks $SCAN_CLK_NAME]这里有几个经验点值得说。第一scan clock的uncertainty要给得比功能时钟大。因为ATE机台到芯片的时钟路径不像板级PLL那么干净jitter和偏斜都更大。我一般setup给0.5ns、hold给0.3ns起步具体看ATE规格。第二source latency不能省。很多人写scan clock只写create_clock就完事忘了ATE到pad这段延迟。这段延迟在低速shift下影响不大但如果shift频率往上拉到50MHz以上不建模就会低估时序压力。第三如果scan clock在芯片内部还经过分频或门控要单独处理。比如有些设计shift clock是从功能时钟分频来的那就要用create_generated_clock而不是create_clock否则时钟树会建错。3.3 模式切换约束set_case_analysis的正确用法shift和capture的切换靠模式信号控制SDC里用set_case_analysis把这些信号固定住让STA只分析当前模式下的路径。# shift模式scan_en1, test_mode1 set_case_analysis 1 [get_ports $SCAN_ENABLE_PORT] set_case_analysis 1 [get_ports $SCAN_MODE_PORT] # 功能时钟在shift模式下应被屏蔽 foreach clk $FUNC_CLK_LIST { set_case_analysis 0 [get_clocks $clk] }这里有个大坑set_case_analysis的作用是让STA忽略被固定的信号上的时序弧但它不会自动屏蔽时钟。如果功能时钟在shift模式下还在翻转STA会同时分析功能路径和shift路径报出一堆无关违例。所以必须显式地把功能时钟在shift模式下关掉或者用set_clock_groups把它们和scan clock设成异步。我通常两个手段一起用set_case_analysis固定模式信号set_clock_groups把scan clock和功能时钟设成异步组双保险。3.4 扫描链路径约束为什么通常不需要逐条约束新手容易犯的错是给每条扫描链单独写约束比如set_max_delay从链头到链尾。实际上绝大多数情况下不需要原因有两个一是扫描链上触发器直连路径天然短工具默认的时序检查已经覆盖二是逐条约束会让SDC膨胀到几千行维护成本极高。真正需要额外约束的是这几种情况链上存在跨时钟域的触发器比如链从clk_a域串到clk_b域这时要设false path或max_delay链上有负沿触发器混正沿触发器时钟沿关系要理清链经过lockup latchlockup的时序要单独检查# 跨时钟域扫描链的false path示例 set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b] \ -through [get_pins -hier *scan_dff*/D] # lockup latch的max_delay set_max_delay 2.0 -from [get_pins lockup_latch*/D] \ -to [get_pins lockup_latch*/Q]4. 从模板到实际约束生成、验证与常见翻车点4.1 模板生成流程与工具衔接模板写好后怎么变成实际约束文件常见流程是模板Tcl 项目配置文件 → 生成脚本 → 输出SDC → 喂给STA工具。我习惯把模板做成一个proc参数通过配置文件读入proc gen_scan_sdc {cfg_file out_file} { source $cfg_file set fp [open $out_file w] puts $fp # Auto-generated scan shift SDC puts $fp # Generated from template v1.0 # ... 逐段生成约束 ... close $fp }生成脚本里加一行注释记录模板版本和生成时间这在后期debug时非常有用——能快速定位是哪个版本的模板生成的约束。和STA工具的衔接上要注意约束加载顺序。scan shift的SDC应该在功能SDC之后加载因为shift模式要覆盖部分功能约束比如关掉功能时钟。如果顺序反了功能约束会把shift设置冲掉。4.2 约束验证怎么确认模板生成的SDC是对的生成完SDC不能直接信必须验证。我一般做三层检查第一层语法和完整性检查。用工具自带的check_timing命令看有没有未约束的时钟、未定义的路径、悬空的端口。这一步能拦住大部分低级错误。第二层时钟关系检查。用report_clocks和report_clock_groups确认scan clock和功能时钟的关系符合预期异步组设置正确。第三层实际路径抽查。挑几条典型扫描链用report_timing -from/-to看时序报告确认检查的是shift路径而不是功能路径。# 验证脚本片段 check_timing -verbose check_timing.rpt report_clocks clocks.rpt report_clock_groups clk_groups.rpt report_timing -from [get_clocks $SCAN_CLK_NAME] \ -to [get_clocks $SCAN_CLK_NAME] -max_paths 10 shift_timing.rpt4.3 五个高频翻车点与排查思路翻车点一scan clock没定义STA用功能时钟分析shift路径。症状是shift模式下报出大量setup违例但实际移位频率很低不该违例。排查report_clocks看scan clock是否存在不存在就是create_clock没生效。翻车点二功能时钟在shift模式下没关两套路径混在一起。症状是违例数量异常多且违例路径里既有scan逻辑又有功能逻辑。排查report_clock_groups看异步组设置检查set_case_analysis是否覆盖了所有功能时钟。翻车点三跨时钟域扫描链没设false path。症状是特定几条链报跨时钟域违例。排查report_timing看违例路径的起点终点时钟如果跨域就补false path。翻车点四lockup latch时序没约束。症状是链上lockup位置报hold违例。排查确认lockup latch的时钟连接补max_delay或min_delay。翻车点五模板参数和实际设计不匹配。比如链长参数写1200但实际最长链1500导致部分链没被覆盖。排查生成后统计约束覆盖的触发器数量和设计实际数量对比。提示这五个翻车点里前两个占了实际问题的七成以上。如果你时间有限优先把时钟定义和模式切换这两块查透。5. 模板工程化版本管理、多corner适配与团队协作5.1 多corner下的模板参数化策略一个项目通常要跑SS、TT、FF多个corner每个corner的时序压力不同。模板要支持corner参数化而不是每个corner复制一份。做法是把corner相关的参数uncertainty、latency、频率抽成corner profile# corner profile示例 array set CORNER_PROFILE { ss_setup_uncertainty 0.6 ss_hold_uncertainty 0.35 ss_shift_freq 15 ff_setup_uncertainty 0.4 ff_hold_uncertainty 0.25 ff_shift_freq 25 }生成时根据目标corner选对应profile主体逻辑不变。这样corner数量增加时模板维护量是线性的而不是指数级的。5.2 模板版本管理与变更追溯模板是要长期维护的资产必须有版本管理。我建议模板文件纳入Git每次修改有commit记录模板头部维护changelog记录每个版本改了什么、为什么改生成的SDC文件里嵌入模板版本号方便追溯# 模板版本信息 set TEMPLATE_VERSION v1.3 set TEMPLATE_CHANGELOG { v1.0 - 初始版本 v1.1 - 增加跨时钟域false path v1.2 - 修正lockup latch约束 v1.3 - 支持多corner profile }这套做法在团队协作里价值很大。当某个项目出现约束问题时能快速定位是模板bug还是项目配置问题。5.3 团队协作中的模板评审要点模板不是一个人写完就完事需要评审。评审时重点看这几项评审项检查内容常见问题参数完整性是否覆盖所有时钟、链、模式漏定义某个时钟域时钟关系异步组、case_analysis是否正确功能时钟未屏蔽路径约束跨域、lockup是否处理false path缺失corner适配profile是否齐全某corner参数缺失可读性注释、命名是否清晰变量名无意义评审通过后模板才能进入项目使用。我见过团队因为跳过评审模板里一个时钟名拼写错误导致整个项目shift约束失效最后靠ATE实测才发现。6. 实测经验几个只有踩过才知道的细节6.1 shift频率不是越低越好很多人觉得shift频率低就安全其实不然。频率太低会导致测试时间暴涨——一条1200级的链20MHz下移位一次要60微秒如果测试向量有几万条总测试时间会到秒级量产测试成本直接上去。我的经验是在ATE能力和时序余量允许的前提下shift频率尽量往高拉。一般先按ATE最高频率跑看时序报告余量如果setup余量大于0.3ns、hold余量大于0.1ns就可以维持余量不够再降频。这样能在测试成本和可靠性之间找到平衡点。6.2 scan enable的时序容易被忽略scan enable信号本身也是时序路径它在shift到capture切换的瞬间要稳定。如果scan enable的到达时间晚于capture clock会导致部分触发器没切回功能模式capture数据错误。SDC里要给scan enable设约束set_max_delay 1.0 -from [get_ports $SCAN_ENABLE_PORT] \ -to [get_pins -hier *scan_en_reg*/D]这个约束容易被漏因为scan enable不是数据路径很多人不把它当回事。但实测中它导致的测试失败并不少见。6.3 模板要留debug钩子模板生成SDC时建议加一些可选的debug输出比如生成一份约束覆盖报告列出每条链被哪些约束覆盖、哪些触发器没被覆盖。这个报告在排查为什么某条链测试失败时非常有用。# 生成约束覆盖报告 proc gen_coverage_report {out_file} { set fp [open $out_file w] puts $fp Chain Coverage Report foreach chain [get_scan_chains] { set dffs [get_pins -hier ${chain}*/Q] puts $fp Chain $chain: [llength $dffs] DFFs } close $fp }6.4 和功能SDC的边界要划清最后强调一个原则scan shift的SDC只负责shift模式capture模式复用功能SDC。不要把capture约束也塞进DFT模板否则模板会变得又大又难维护而且和功能约束重复。正确的分工是DFT模板管shift时钟、模式切换、跨域false path功能SDC管capture和正常功能。两者通过模式信号切换各管各的边界清晰。这套模板思路我在几个项目里用过从28nm到7nm都跑得通核心逻辑不变变的只是参数和corner配置。真正花时间的不是写模板本身而是把项目的时钟架构和扫描链结构摸清楚——模板只是把这些理解固化下来。如果你刚开始做DFT约束建议先手工写一版能跑通的约束理解每条约束为什么这么写再抽象成模板这样模板才不会是空中楼阁。