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,累死人。