DFT Compiler 实战:扫描链没有连到指定端口的排查与修复全记录
专栏:iLoveIC Aug. 7, 2026, 4:11 p.m. 11 阅读
DFT 插入完成后检查网表,发现扫描链并没有按约束复用指定的端口,DC 反而自作主张新加了几个顶层端口……

工具:Synopsys DFT Compiler (DC Topographical) O-2018.06-SP1
设计:digital_top(显示驱动芯片数字顶层,约 22k 扫描单元,DFTMAX 压缩)
关键词:insert_dft、set_dft_signal、hookup pin、define_test_mode、Autofix、TestData

一、问题现象

DFT 插入完成后检查网表,发现扫描链并没有按约束复用指定的端口,DC 反而自作主张新加了几个顶层端口:

  • data_sourcehandler_1:DC 自建的扫描时钟端口,扫描链的 MasterClock 全挂在它们上面,而我们指定的扫描时钟 P2D_SCAN_CLOCK 被晾在一边;

  • test_mode:DC 自建的模式控制端口,而我们指定的 TestMode 信号(内部 u_AND_scan_mode/Z,即 ATPG_EN)和压缩使能 P2D_SCAN_COMPRESS_EN 没有被用作模式编码;

  • 设计里特意留的两个 hookup buffer(u_buf_vloading_0/1,分别挂在 P2D_SCAN_CLOCKP2D_SCAN_COMPRESS_EN 上、输出故意悬空留给 DC 接)输出依然是空的。

一句话:DC 新加了 pin,没有复用我们指定的 scan clock、compression enable 等信号。

二、初版约束脚本的"案发现场"

# —— 扫描时钟:spec 视图那行把 -view 写错了(笔误)——
set_dft_signal -view existing_dft -hookup_pin {u_buf_vloading_0/Z} -type ScanClock -timing [list 45 55]
#set_dft_signal -view spec -port {P2D_SCAN_CLOCK} -type ScanClock          # 被注释
set_dft_signal -view spec -hookup_pin {u_buf_vloading_0/Z} -type ScanClock   # ← 实际是 -view existing_dft,笔误

# —— 压缩使能:只声明在悬空的 hookup pin 上 ——
#set_dft_signal -view spec -port {P2D_SCAN_COMPRESS_EN} -type TestControl -active_state 1  # 被注释
set_dft_signal -view spec -hookup_pin {u_buf_vloading_1/Z} -type TestControl -active_state 1

# —— 自定义模式被注释(且引用了不存在的端口 P2D_CPRES)——
#define_test_mode my_base1 -usage scan -encoding {P2D_ATPG 1 P2D_CPRES 0}
#define_test_mode scan_compression1 -usage scan_compression -encoding {P2D_ATPG 1 P2D_CPRES 1}

set_scan_configuration -chain_count 4          # 没有 -test_mode
set_scan_compression_configuration -chain_count 48 -xtolerance default

三、排查过程:顺藤摸瓜

3.1 先澄清一个"假问题":解压器的 sel 引脚

检查网表时发现压缩解压器的例化是:

digital_top_SCCOMP_DECOMPRESSOR ... (
    .din({P2D_SCAN_DATA_IN[1], P2D_SCAN_DATA_IN[0]}),        // 只有 2 路数据
    .sel({P2D_SCAN_DATA_IN[3], P2D_SCAN_DATA_IN[2]}) );       // 另外 2 个 ScanDataIn 被拿去当模式选择

日志对应行:Architecting Load Decompressor (version 5.8),Number of inputs/chains/internal modes = 4/48/4

48 条内部链对 4 个扫描输入(12:1),DFTMAX 生成了带 4 个 internal modes 的可重构解压器,需要 log2(4)=2 条 select,于是从 ScanDataIn 端口池里拿了两个当 sel。这是 DFTMAX 自适应扫描的标准架构行为,不是连接错误——SPF 里 sel 以 sccompin 伪链形式声明,TetraMAX 可以正常处理。如果想 4 口全做数据,需要降低链/输入比或增加端口。

3.2 新端口从哪来:对比 pre-DFT 网表

data_sourcehandler_1test_mode 在 pre-DFT 网表中完全不存在,是 insert_dft 阶段新建的。同时发现两个 hookup buffer 在 pre-DFT 网表里就输出悬空(BUFM1TM u_buf_vloading_0 ( .A(P2D_SCAN_CLOCK) ); 没有 .Z)。

3.3 关键认知:声明 hookup pin ≠ DC 会去连接

这是本次排查最重要的认知。set_dft_signal 的视图语义:

  • existing_dft 视图:告诉 DRC"设计里已经有这个信号",只用于协议和规则检查,永远不会产生任何物理连接

  • spec 视图:告诉 insert_dft"请按这个建",只有它才可能产生连接;

  • 即使 spec 视图声明了,也只有当 DC 插入的逻辑确实需要消费这个信号时才会接线。TestControl/TestMode 这类声明性信号,如果没有结构引用它们(比如 define_test_mode 的编码),声明完了就只是报告里的一行字。

对照初版脚本:ScanClock 的 spec 视图因笔误缺失 → DC 找不到指定扫描时钟 → 自建端口;TestControl 挂在悬空 pin 上且没有任何结构消费 → DC 自建 TCM 和 test_mode 端口(端口名来自 test_mode_port_naming_style "test_mode%s")。

四、修复迭代:七轮跑通,每轮攻克一类错误

改脚本—跑 make dftins(约 9 分钟)—看报错—查 man page—再改,共七轮:

轮次改动结果/新报错
1define_test_mode(P2D_SCAN_COMPRESS_EN 编码)+ -test_mode 关联 chain_count/scan_path/压缩配置TESTXG-3:ScanClock 用 hookup pin 必须同时指定一个 port;UIT-1800:encoding 引用的端口必须先声明为 TestMode
2P2D_SCAN_COMPRESS_EN 改 TestMode;ScanClock 用 port+hookup 组合规格全部被 Accepted,但 DC 自建了无编码的 Internal_scan 当压缩模式基链 → TCM 报 Conflicting pin values,DRC 出现 S1 链阻断(FATAL),my_base1 的链是空的
3压缩配置加 -base_mode my_base1基链模式正确关联(日志:Architecting scan compression mode scan_compression1 with base mode my_base1),S1 消除、DRC 通过;但 data_source/handler_1 仍在
4去掉 TestMode 的 -active_state;ScanClock 改纯 -port 形式自建时钟端口仍在;buffer 仍悬空(man page:hookup pin 找不到 proper global driver 就不 stitch)
5spec 视图加 TestData 声明UIT-663:同一端口不能在 spec 视图同时有两种信号类型
6ScanClock 只留 existing_dft(带 timing),TestData 只放 spec(官方示例写法);P2D_SCAN_COMPRESS_EN TestMode 只留 specdata_source/handler_1/test_mode* 全部消失,4 条基链 + 48 条压缩链的 MasterClock 全部变为 P2D_SCAN_CLOCK
7删掉 dft_prepare 里手插的两个 vloading buffer(DC 直连端口即可)✅ 重跑确认:零新增端口,TCM 译码输入直连 P2D_SCAN_COMPRESS_EN,DRC 通过

最终生效的约束写法

# 扫描时钟:existing_dft 声明 ScanClock(DRC/协议认识这个时钟)
set_dft_signal -view existing_dft -port {P2D_SCAN_CLOCK} -type ScanClock -timing [list 45 55]
# Autofix 时钟源:spec 声明 TestData(insert_dft 建新逻辑时复用它)
set_dft_signal -view spec -type TestData -port {P2D_SCAN_CLOCK}

# 压缩使能/模式编码端口:spec 视图 TestMode,不带 active_state
set_dft_signal -view spec -port {P2D_SCAN_COMPRESS_EN} -type TestMode

# 自定义测试模式
define_test_mode my_base1          -usage scan             -encoding {P2D_SCAN_COMPRESS_EN 0}
define_test_mode scan_compression1 -usage scan_compression -encoding {P2D_SCAN_COMPRESS_EN 1}

# define_test_mode 之后,这些命令必须显式带 -test_mode 归属
set_scan_configuration -chain_count 4 -test_mode my_base1
set_scan_path -view spec -test_mode my_base1 chain0 -scan_enable P2D_SCAN_EN \
    -scan_data_in P2D_SCAN_DATA_IN[0] -scan_data_out D2P_SCAN_DATA_OUT[0]
# ... chain1~3 同理 ...
set_scan_compression_configuration -chain_count 48 -xtolerance default \
    -test_mode scan_compression1 -base_mode my_base1

最终链报告

I chain0  5702  P2D_SCAN_DATA_IN[0]  D2P_SCAN_DATA_OUT[0]  P2D_SCAN_EN  P2D_SCAN_CLOCK
I chain1  5702  P2D_SCAN_DATA_IN[1]  D2P_SCAN_DATA_OUT[1]  P2D_SCAN_EN  P2D_SCAN_CLOCK
I chain2  5702  P2D_SCAN_DATA_IN[2]  D2P_SCAN_DATA_OUT[2]  P2D_SCAN_EN  P2D_SCAN_CLOCK
I chain3  5701  P2D_SCAN_DATA_IN[3]  D2P_SCAN_DATA_OUT[3]  P2D_SCAN_EN  P2D_SCAN_CLOCK

链长从原先的 7555/7555/7554/143(严重失衡)变成 5702/5702/5702/5701——时钟统一后时钟域割裂也顺带消除了。

五、知识点总结(都来自 man page,建议收藏)

  1. spec vs existing_dft:existing_dft 只读不产生连接;spec 才指导 insert_dft 建设。写约束时先想清楚"我在描述现状,还是在下建设指令"。

  2. hookup pin 不是许愿池man set_dft_signal):"The hookup pin specified should be either on a black box or should have a proper global driver. If a proper global driver is not found, insert_dft will not stitch to the specified hookup pin." 悬空 buffer 输出就不是 proper global driver 场景能覆盖的;而且 DC 默认会"跳到端口核心侧、跳过 buffer"去连接,所以专门插 buffer 当 hookup 点经常是多此一举。

  3. encoding 端口必须是 TestMode portUIT-1800),且官方示例里只在 spec 视图、不带 active_state 声明。

  4. define_test_mode 之后的命令归属陷阱man define_test_mode):一旦定义了自定义模式,set_scan_configuration set_dft_signal set_scan_compression_configuration 不带 -test_mode 就只作用于最后定义的那个模式,要用 -test_mode all 或显式指定。

  5. 压缩模式必须绑定基链模式set_scan_compression_configuration -test_mode <压缩模式> -base_mode <基链模式>,否则 DC 自建一个无编码的 Internal_scan 当基链,引发 TCM 编码冲突甚至 S1 链阻断。

  6. Autofix 的时钟源要预声明 TestDataman set_autofix_configuration):"If no test data signal is specified, Autofix creates a new ScanClock or Reset port depending on the fixing type." 自建时钟端口的真凶就是它。标准写法:同一端口 existing_dft 视图声明 ScanClock(带 timing)+ spec 视图声明 TestData。

  7. 同一端口多角色要拆视图UIT-663):同一视图内一个端口只能有一种信号类型;ScanClock(existing_dft) + TestData(spec) 是官方推荐拆法。

  8. DFTMAX 自适应解压器会占用扫描输入口当 sel:链/输入比高时自动生成带 internal modes 的可重构解压器,sel 从 ScanDataIn 池里取——看到 din 数量比预期少不要慌,先算一下比例。

六、遗留事项

  • my_base1 在 TCM 模式向量生成时报 Conflicting pin values in top level vectors:协议/向量层告警,网表结构不受影响(TCM 译码硬件已验证正确),待后续结合 TetraMAX 协议再生时一并处理;

  • TEST-337(DC 试探性征用复位端口 A2D_RST_PORB 被拒):架构探测的良性告警;

  • TESTXG-53:internal pins flow 下写出的协议"不准确不可用",给 ATPG 用前需在非 internal pins flow 下重新生成。

七、心得

这次排障最大的体会是:DC 的 DFT 约束体系里,"声明"和"连接"之间隔着好几层语义——视图(spec/existing_dft)、信号类型(ScanClock/TestData/TestMode/TestControl)、模式归属(-test_mode/-base_mode)、以及 Autofix 的独立取数逻辑。每违反一条,DC 不会报错拒绝,而是"聪明地"自建端口、自建模式、自建时钟,结果网表看起来能跑,实际上处处不是你要的。排查这类问题最有效的方法就是:netlist 对比(pre-DFT vs post-DFT)→ 报告交叉验证(dft_signal/dft_path/autofix_config)→ 逐条啃 man page 的精确语义——Synopsys 的文档写得很清楚,只是你得被逼到那份上才去读它。

感谢阅读,更多文章点击这里:【专栏:iLoveIC】
最新20篇