4、扫描链综合与实现:DC/Genus中扫描链插入命令、扫描链的 stitching、扫描链的时序约束、扫描链的验证

好,咱们进入第四章。扫描链的综合与实现,这可以说是 DFT 流程里最「硬核」的一环了。你想想看,前面我们规划好了扫描链的结构,选好了扫描单元,但怎么把这些东西真正塞进芯片里,让它能正常工作?这就是本章要聊的。

我个人习惯把这一步叫做「把设计变成可测试的设计」。说白了,就是让每个触发器都能被「串」起来,形成一个可以移入移出数据的链条。嗯,这里要注意,这个过程不是简单的连线,它涉及到工具的使用、时序的考量,还有大量的验证工作。

4.1 DC/Genus 中的扫描链插入命令

目前主流的综合工具,Synopsys 的 DC 和 Cadence 的 Genus,都提供了完善的扫描链插入功能。我最早用的是 DC,后来项目转到 Genus 上,发现核心思想其实是一样的,只是命令名和选项略有不同。

在 DC 里,核心命令是 insert_dft。但在跑这个命令之前,你得先告诉工具你的设计意图。比如,你要定义哪些是扫描时钟,哪些是扫描使能,还有扫描输入输出端口。

一个典型的 DC 脚本片段是这样的:

# 设置扫描模式
set_scan_configuration -style multiplexed_flip_flop
set_scan_configuration -chain_count 4

# 定义扫描端口
create_port -direction in  scan_clk
create_port -direction in  scan_en
create_port -direction in  scan_in[0]
create_port -direction out scan_out[0]

# 指定扫描时钟
set_dft_signal -view existing_dft -type ScanClock -port scan_clk -timing {45 55}

# 指定扫描使能
set_dft_signal -view existing_dft -type ScanEnable -port scan_en -active_state high

# 指定扫描输入输出
set_dft_signal -view existing_dft -type ScanDataIn -port scan_in[*]
set_dft_signal -view existing_dft -type ScanDataOut -port scan_out[*]

# 执行 DFT 插入
insert_dft

在 Genus 里,流程类似,但用的是 set_dft_configurationinsert_dft 命令。我记得有一次在 Genus 上做项目,因为没注意 -chain_count 的默认值,结果工具只给我插了一条链,后来查了半天才发现是配置没写对。

我的小技巧: 不管用哪个工具,建议在 insert_dft 之前,先跑一遍 check_dft_rules(DC)或 check_dft(Genus)。这能提前发现很多低级错误,比如时钟没定义、扫描使能没连上等等。

4.2 扫描链的 Stitching

Stitching,直译就是「缝合」。你想想看,就是把一个个扫描单元用线串起来。但怎么串?谁在前谁在后?这里面有讲究。

工具默认会做优化,比如根据物理位置就近连接,以减少绕线。但有时候,我们得手动干预。比如,有些关键路径上的触发器,我们希望它们离得近一点,以减少扫描链上的延迟。

在 DC 里,你可以用 set_scan_elementset_scan_path 来指定链的顺序。比如:

# 手动指定一条扫描链的顺序
set_scan_path test_chain1 -view spec -scan_elements { \
    U_FF1/Q U_FF2/Q U_FF3/Q U_FF4/Q \
} -scan_data_in scan_in[0] -scan_data_out scan_out[0]

不过,我一般不建议手动指定所有链的顺序,除非你有非常特殊的需求。比如,我曾经在一个高速设计中,为了满足时序,不得不把几个关键触发器放在链的末端,以减少它们之间的组合逻辑延迟。

Stitching 完成后,工具会生成一个 dft_compressedscan_chain 的报告。我会仔细检查这个报告,确保每条链的长度差不多,没有出现某个链特别长、其他链特别短的情况。因为链长不均会导致测试时间变长,说白了就是浪费钱。

4.3 扫描链的时序约束

很多人觉得扫描链是测试模式,时序要求不高。其实这是个误区。扫描链在 shift 模式下,时钟频率可能不高,但 scan_en 的切换时序、扫描时钟的 skew,都会影响芯片能否正常工作。

我建议在综合时,就为扫描模式单独设置一套时序约束。比如:

# 创建扫描模式的时钟
create_clock -name scan_clk -period 100 [get_ports scan_clk]

# 设置扫描输入输出的延迟
set_input_delay -clock scan_clk 10 [get_ports scan_in[*]]
set_output_delay -clock scan_clk 10 [get_ports scan_out[*]]

# 设置扫描使能的时序
set_false_path -from [get_ports scan_en] -to [all_registers -data_pins]

这里要注意,scan_en 在 shift 模式下是静态的,所以我们可以把它设成 false path。但在 capture 模式下,scan_en 的切换必须满足 setup/hold 时间。嗯,这个细节很容易被忽略。

避坑指南: 我曾经在一个项目中,因为 scan_en 的时序约束没设好,导致芯片在测试时 scan_en 信号毛刺,把数据冲乱了。后来花了整整两天才定位到问题。所以,请务必检查 scan_en 的时序。

4.4 扫描链的验证

插入完扫描链,不等于万事大吉。你得验证它能不能正常工作。验证分两步:功能验证和时序验证。

功能验证,就是跑仿真。我会写一个简单的 testbench,往 scan_in 里灌一串数据,然后看 scan_out 能不能在预期的周期后输出同样的数据。比如:

// 简单的扫描链测试
initial begin
    // 复位
    reset_n = 0;
    #100 reset_n = 1;

    // 进入 shift 模式
    scan_en = 1;

    // 灌入数据
    for (int i=0; i<100; i++) begin
        @(posedge scan_clk);
        scan_in[0] = $random;
    end

    // 切换到 capture 模式
    scan_en = 0;
    @(posedge scan_clk);

    // 再切回 shift 模式,把数据移出来
    scan_en = 1;
    for (int i=0; i<100; i++) begin
        @(posedge scan_clk);
        // 检查 scan_out
    end
end

时序验证,就是跑 STA。我会用 PrimeTime 或 Tempus,加载扫描模式的约束,检查 setup 和 hold。这里要特别注意扫描时钟的 skew,如果 skew 太大,可能会导致 shift 时数据传错。

我记得有一次,STA 报告显示一条扫描链的 hold 违例很严重。查了半天,发现是扫描时钟的 tree 没做好,导致两个触发器之间的时钟延迟差太大。后来重新做了时钟树综合,才把问题解决。

最后,我还会跑一遍 dft_drc(设计规则检查)。这个工具会检查扫描链的完整性,比如有没有浮空的扫描输入、有没有未连接的扫描输出等等。这些低级错误,一旦流片,就是灾难。

总结一下: 扫描链的综合与实现,不是简单的「一键插入」。它需要你理解工具的命令、掌握 stitching 的技巧、做好时序约束,并且通过充分的验证。每一步都有坑,但只要你细心,都能避开。

好了,这一章就到这里。下一章我们会聊扫描链的测试向量生成,也就是 ATPG。到时候我会分享一些生成高效向量的经验,敬请期待。