3、扫描链设计(Scan Chain)
扫描链,说白了就是给芯片装上一套「体检系统」。
我刚开始做DFT那会儿,总觉得扫描链就是简单地把寄存器串起来。后来踩过坑才明白——这里面的门道,远比想象中多。
3.1 扫描链原理
正常工作时,寄存器各司其职。但测试时,我们需要把它们串成一条链。
为什么会这样?因为芯片内部节点太多,直接测根本测不过来。你想想看,几百万个节点,每个都拉一根测试针?不现实。
所以就有了扫描链的思路:
- 正常工作模式:每个寄存器独立工作,各管各的
- 扫描移位模式:所有寄存器串成一条大链,数据像流水一样传下去
- 捕获模式:让组合逻辑跑一下,把结果抓到寄存器里
我在项目中遇到过一种情况:某次芯片测试覆盖率死活上不去,查了半天发现是扫描链结构设计不合理,导致某些路径根本测不到。嗯,从那以后我设计扫描链时都会先画个拓扑图。
核心要点:扫描链的本质,是把时序电路在测试时「降维」成组合电路。这样就能用ATPG工具自动生成测试向量了。
3.2 扫描触发器
扫描触发器,就是普通触发器加了个「二选一」开关。
结构其实很简单:
// 扫描触发器结构示意
module scan_flipflop (
input clk,
input rst_n,
input d, // 正常数据输入
input si, // 扫描输入
input se, // 扫描使能
output q, // 正常输出
output so // 扫描输出(通常就是q)
);
reg q_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q_reg <= 1'b0;
else if (se)
q_reg <= si; // 扫描模式
else
q_reg <= d; // 正常模式
end
assign q = q_reg;
assign so = q_reg; // 扫描输出就是寄存器输出
endmodule
这里有个细节:so 和 q 通常是同一个信号。但有些设计会单独拉一个扫描输出缓冲,避免影响正常路径时序。
我个人习惯用带 MUX 的扫描触发器,而不是用独立的MUX加触发器。为什么?因为集成在一起面积更小、时序更好。你想想看,多一个MUX就多一段走线延迟。
经验之谈:选择扫描触发器时,要注意它的建立时间和保持时间。我曾经用过某家工艺的扫描触发器,扫描模式下的建立时间比正常模式多了30%,结果导致扫描频率上不去。
3.3 扫描输入输出
扫描输入输出,就是芯片的「体检入口」和「体检出口」。
| 信号 | 方向 | 说明 |
|---|---|---|
| scan_in | 输入 | 测试数据从这儿灌进去 |
| scan_out | 输出 | 测试结果从这儿吐出来 |
| scan_enable | 输入 | 控制扫描模式/正常模式 |
| scan_mode | 输入 | 全局扫描模式控制(可选) |
设计扫描输入输出时,有几个坑要注意:
- IO复用:扫描输入输出通常复用功能IO。测试时切过去,正常工作时切回来
- 电平转换:如果扫描链跨电压域,要加电平转换器
- ESD保护:扫描IO也要有ESD保护,别省这个
我曾经在一个项目中,因为扫描输出没有加足够的驱动能力,导致测试机台读到的信号全是毛刺。查了两天才发现是驱动强度不够。从那以后,我设计扫描IO时都会留20%的余量。
警告:扫描输入输出不要共用同一个IO。否则你没法同时灌数据和读结果,测试效率会大打折扣。
3.4 扫描使能控制
扫描使能(scan_enable),是整个扫描链的「总开关」。
它的控制逻辑其实不复杂:
// 扫描使能控制示意
assign scan_mode = scan_enable & test_mode;
// 每个扫描触发器的选择信号
// se = 1: 扫描移位模式
// se = 0: 正常/捕获模式
但实际设计中,扫描使能的控制要精细得多:
- 全局使能:所有扫描链共用一个使能信号。简单,但灵活性差
- 分组使能:不同扫描链用不同的使能。灵活,但控制复杂
- 时钟门控:扫描使能要和时钟配合好,避免时序问题
我个人建议用分组使能。为什么?因为不同模块的测试需求不一样。比如CPU核需要高频测试,而外设接口可能只需要低频测试。分组使能可以灵活控制。
这里有个关键点:扫描使能的切换时机。
你想想看,如果扫描使能在时钟沿附近变化,会怎么样?
对,会产生毛刺。所以扫描使能通常要经过同步处理:
// 扫描使能同步处理
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
se_sync1 <= 1'b0;
se_sync2 <= 1'b0;
end else begin
se_sync1 <= scan_enable;
se_sync2 <= se_sync1;
end
end
assign se = se_sync2; // 同步后的扫描使能
避坑指南:我曾经在一个项目中,扫描使能没有做同步处理,结果测试时频繁出现数据错误。后来加了三级同步寄存器,问题就解决了。记住:跨时钟域的信号,一定要同步!
3.5 扫描链设计实战要点
说了这么多理论,来点实际的。
设计扫描链时,我通常会按以下步骤来:
- 确定扫描链数量:根据芯片规模和测试时间要求来定。一般每条链不超过10000个触发器
- 分配扫描触发器:把功能相关的寄存器尽量放在同一条链上
- 插入扫描单元:用工具自动替换,但人工检查关键路径
- 连接扫描链:注意走线长度,避免跨模块长线
- 验证扫描链:跑仿真,确保每条链都能正常工作
嗯,这里要特别强调一下验证。我见过太多人设计完扫描链就直接交出去了,结果测试时发现链根本不通。所以我的习惯是:每设计完一条链,先跑个简单的「链测试」——灌一串0101进去,看能不能原样吐出来。
小技巧:扫描链的测试频率通常比功能频率低。但也不要太低,否则测不出时序问题。我一般用功能频率的1/3到1/2作为扫描频率。
最后说一句:扫描链设计不是一锤子买卖。随着芯片迭代,扫描链也要跟着优化。保持设计文档的更新,比什么都重要。