3、扫描链的插入流程:综合前的准备、RTL代码规范、DFT编译器插入流程
好,咱们今天聊聊扫描链到底怎么插进去的。很多人觉得这步就是跑个脚本完事,其实不然。我见过太多项目,前期RTL写得随意,到了DFT阶段各种报错,回头改代码改到崩溃。说白了,扫描链插入这件事,功夫在诗外。
3.1 综合前的准备:别急着跑工具
在打开DC或者Genus之前,有几件事你得先确认好。我个人习惯列个清单,挨个打勾。
准备工作清单:
- 时钟结构确认:所有时钟域都理清楚了吗?有没有门控时钟?有没有异步时钟?扫描时钟怎么接?
- 复位策略:同步复位还是异步复位?扫描模式下复位怎么处理?
- IO端口规划:扫描输入输出管脚够不够?要不要做压缩?
- 库文件准备:综合库、DFT库、测试库,版本对不对得上?
嗯,这里要注意一点。很多新手上来就急着写脚本,结果发现库文件里没有扫描触发器。你想想看,那还玩什么?所以第一步,先确认你的标准单元库里有带扫描功能的寄存器。我遇到过一回,库文件版本更新后,扫描单元的名字变了,脚本没改,跑出来全是普通寄存器,那叫一个尴尬。
3.2 RTL代码规范:写代码时就想着DFT
RTL代码写得好不好,直接决定了DFT流程顺不顺。我经常跟设计工程师说,你写代码的时候,脑子里得想着测试的事。
3.2.1 避免不可控的逻辑
有些写法在功能上没问题,但到了测试模式就变成黑盒子了。比如:
// 不好的写法:内部振荡器
always @(posedge osc_out) begin
counter <= counter + 1;
end
// 好的写法:测试模式下可旁路
assign test_clk = scan_mode ? scan_clk : osc_out;
always @(posedge test_clk) begin
counter <= counter + 1;
end
说白了,测试模式下,所有时钟都得可控。内部产生的时钟、分频时钟、门控时钟,统统要能旁路到扫描时钟上。
3.2.2 三态总线的处理
三态总线在DFT里是个麻烦事。我建议的做法是:
- 测试模式下,所有三态输出强制为高阻
- 或者加一个测试专用的使能信号,把总线隔离
小技巧:在RTL里加一个`scan_mode`信号,所有DFT相关的逻辑都靠它切换。这样功能代码和测试代码分得清清楚楚,后期维护也方便。
3.2.3 异步逻辑的处理
异步复位、异步置位、异步握手,这些在DFT里都是坑。我曾经在一个项目里,有个异步复位信号没处理好,扫描链死活串不起来。后来发现是复位信号在测试模式下一直有效,把寄存器都复位了,数据根本传不过去。
正确的做法是:测试模式下,让异步复位信号失效。比如:
assign test_rst_n = scan_mode ? 1'b1 : rst_n;
3.3 DFT编译器插入流程:从脚本到网表
好了,准备工作做完了,RTL也检查过了,现在可以跑工具了。我用的是Synopsys的DFT Compiler,流程大致如下:
3.3.1 读入设计和库
set target_library "sc9_cln40g_base.db"
set link_library "* $target_library"
read_verilog top.v
link
current_design top
这里有个细节:库文件的顺序很重要。我习惯把扫描库放在前面,这样工具会优先使用扫描单元。
3.3.2 配置DFT
set_dft_configuration -scan_compression enable
set_scan_configuration -chain_count 8
set_scan_configuration -clock_mixing no_mix
set_scan_configuration -add_lockup enable
嗯,这里要解释一下几个关键参数:
| 参数 | 说明 | 我的建议 |
|---|---|---|
| chain_count | 扫描链数量 | 根据IO管脚和测试时间折中,一般8-32条 |
| clock_mixing | 是否允许跨时钟域 | 新手建议no_mix,省心 |
| add_lockup | 是否加锁存器 | 跨时钟域时一定要加 |
3.3.3 创建测试协议
create_test_protocol
dft_drc
跑完这两步,你会看到一堆DRC报告。别慌,一条条看。我见过有人看到几百条warning直接跳过,结果后面跑仿真全挂。DRC里的每个violation都得认真对待。
警告:DRC报告里的"Unconstrained clock"和"Uncontrolled pin"是最常见的两类问题。前者说明时钟没约束好,后者说明某个管脚在测试模式下不受控。这两个不解决,后面都是白搭。
3.3.4 插入扫描链
insert_dft
dft_drc -coverage_estimate
插入完成后,工具会给你一个覆盖率估计。我个人习惯目标是98%以上。如果低于95%,就得回头看看是不是有些模块没覆盖到。
3.3.5 输出网表和协议
write -format verilog -hierarchy -output top_scan.v
write_test_protocol -output top_scan.spf
这两个文件就是后续ATPG的输入。网表是带扫描的,协议文件告诉ATPG工具怎么控制扫描链。
3.4 常见问题与避坑指南
做DFT这么多年,踩过的坑不少。挑几个典型的说说:
- 扫描链长度不均:8条链,有的1000个寄存器,有的5000个。测试时间由最长的链决定。我建议插入前先看看各模块的寄存器数量,手动分配一下。
- 时钟树不平衡:扫描模式下时钟树和功能模式不一样,可能导致hold violation。解决办法是在扫描路径上加delay cell。
- IO复用冲突:扫描IO和功能IO共用管脚,结果功能模式下把扫描时钟拉低了。这个得跟顶层设计的人提前沟通好。
我的经验:每次做完DFT插入,我都会跑一遍仿真。不是跑ATPG,就是简单的shift测试,看看数据能不能从SI传到SO。这一步花不了多少时间,但能发现很多问题。我曾经有一次,仿真发现某条链的SO一直为0,查了半天,原来是那个管脚在顶层被拉死了。
好了,扫描链插入流程就这些。说白了,前期准备占70%,工具操作占30%。RTL写得规范,后面就顺风顺水。要是RTL写得随意,那后面就是各种补丁和workaround,累死人。