第三章:扫描链设计(Scan Chain)
扫描链,说白了就是给芯片装上一套「体检系统」。
我刚开始接触DFT时,觉得这玩意儿挺玄乎的。后来做多了才明白,它其实就是把普通寄存器串起来,让测试时能像串糖葫芦一样,把数据一颗颗塞进去、再一颗颗掏出来检查。
3.1 扫描链原理
正常工作时,每个寄存器各司其职。但测试时,我们需要知道每个寄存器的状态对不对。怎么办?
答案很简单:给每个寄存器加个「开关」。
这个开关就是扫描模式。在扫描模式下,所有寄存器首尾相连,形成一条长长的移位寄存器链。测试数据从链头灌入,经过每个寄存器,最后从链尾输出。
我打个比方:就像一排士兵,平时各自站岗。集合时,他们手拉手连成一排,信息就能从第一个传到最后一个。
扫描链的核心操作就两个:
- 移位(Shift):把测试数据串行灌入每个寄存器
- 捕获(Capture):让芯片跑一个时钟周期,把组合逻辑的结果锁存到寄存器
然后,再把捕获到的结果移位出来,和预期值比对。这就是扫描测试的全部秘密。
关键点:扫描链的测试覆盖率,取决于你能控制多少内部节点。覆盖率越高,漏掉的缺陷就越少。
3.2 扫描触发器类型
扫描触发器,就是加了扫描功能的普通触发器。我见过好几种,常用的就下面这几类:
| 类型 | 结构特点 | 面积开销 | 适用场景 |
|---|---|---|---|
| Muxed-D型 | 数据路径加一个2选1MUX | 约10-15% | 最常用,通用性强 |
| LSSD型 | 双锁存器结构,无时钟竞争 | 约20-30% | 高性能、高可靠性设计 |
| 时钟门控型 | 集成时钟门控逻辑 | 约15-20% | 低功耗设计 |
Muxed-D型 是我用得最多的。它就是在普通D触发器前加一个MUX,选择信号是扫描使能(Scan Enable)。正常模式选D端,扫描模式选SI(Scan Input)端。
嗯,这里要注意:Muxed-D型有个坑——时钟竞争。如果扫描使能切换时机不对,数据可能出错。我早期一个项目就吃过这个亏,后来老老实实加了时序约束才搞定。
LSSD型 比较特殊。它用两个锁存器代替一个触发器,一个叫L1,一个叫L2。L1负责捕获,L2负责移位。好处是彻底避免了时钟竞争,坏处是面积大。
我记得有个客户做军工芯片,要求零缺陷,硬性规定必须用LSSD。那项目面积预算直接爆了,但测试覆盖率确实漂亮,接近100%。
时钟门控型 是低功耗设计的宠儿。它把时钟门控逻辑和扫描功能整合在一起,既省电又省面积。不过,这种触发器在ATPG时容易出问题,需要额外处理。
我的建议:除非有特殊要求,否则优先选Muxed-D型。它成熟、稳定、工具支持好。LSSD留给那些对可靠性有执念的项目。
3.3 扫描链插入流程
扫描链插入,听起来高大上,其实就三步走:
- 准备网表:把综合后的门级网表拿过来
- 替换触发器:把所有普通触发器换成扫描触发器
- 连接成链:把扫描触发器的SI和SO首尾相连
我用一个简单的例子来说明。假设我们有三个触发器FF1、FF2、FF3:
// 原始网表(普通触发器)
DFF FF1 (.D(d1), .CK(clk), .Q(q1));
DFF FF2 (.D(d2), .CK(clk), .Q(q2));
DFF FF3 (.D(d3), .CK(clk), .Q(q3));
// 扫描链插入后
SDFF FF1 (.D(d1), .SI(scan_in), .SE(scan_en), .CK(clk), .SO(n1));
SDFF FF2 (.D(d2), .SI(n1), .SE(scan_en), .CK(clk), .SO(n2));
SDFF FF3 (.D(d3), .SI(n2), .SE(scan_en), .CK(clk), .SO(scan_out));
你看,FF1的SO连到FF2的SI,FF2的SO连到FF3的SI。这样,scan_in的数据就能一路传到scan_out。
实际项目中,扫描链插入通常用EDA工具自动完成。我常用的流程是:
- 用Synopsys的DFT Compiler做综合和扫描插入
- 用Mentor的Tessent做ATPG和仿真验证
- 最后用Cadence的Conformal做形式验证,确保功能没被改坏
不过,工具不是万能的。我曾经遇到一个项目,工具自动插了100条扫描链,结果有一条链的时序怎么也修不好。最后我手动调整了那条链的寄存器顺序,把关键路径上的寄存器放在链尾,问题才解决。
避坑指南:扫描链插入后,一定要做后仿真。我曾经跳过这一步,结果流片回来发现有一条链的SO和SI接反了。那叫一个惨,整批芯片报废。
扫描链的条数和长度怎么定?这取决于你的测试时间和芯片面积。一般来说:
- 链越长,测试时间越短(因为并行度高了),但每条链的时序压力越大
- 链越短,时序好修,但测试时间会变长
我个人习惯是:先把所有寄存器按功能模块分组,每组内部连成一条链。这样既保证了时序,又方便调试。如果某条链的寄存器太多(超过500个),我会再拆成两条。
最后,别忘了给扫描链加上测试时钟和测试复位。很多新手会漏掉这个,导致扫描测试时寄存器状态不可控。
总结一下:扫描链设计,核心就是「串起来、灌进去、读出来」。选对触发器类型,走对插入流程,再留个心眼做验证,基本就稳了。
可可精品资料库