在 IC 设计项目中,管理库依赖关系是确保设计完整性和可移植性的关键环节。本文介绍一个强大的 SKILL 工具,能够快速扫描指定库列表,识别所有引用了外部库的实例,帮助进行 IP 交付检查、依赖关系审计和库迁移准备。
需求场景
在实际工作中,我们经常遇到需要检查库依赖关系的场景:
场景 1:IP 交付前检查
1
2
需求:确保 IP 库只引用了允许的标准工艺库
问题:如何快速发现所有非法的外部库引用?
场景 2:项目依赖关系审计
1
2
需求:审查整个项目引用了哪些外部库
问题:手动检查数千个 cellview 工作量巨大
场景 3:库迁移准备
1
2
需求:迁移库前识别所有外部依赖
问题:需要知道哪些外部库必须一并迁移
场景 4:设计规范检查
1
2
需求:验证设计是否符合库引用规范
问题:快速发现违反规则的引用
工具功能
CCSFindExternalRefs.il 提供了自动化的库依赖关系检查功能:
| 功能 | 说明 |
|---|---|
| 灵活的库列表 | 支持两个库列表:待扫描库 (List A) 和允许引用库 (List B) |
| 智能过滤 | 只报告 List A 中引用了不在 List B 中的外部库 |
| View 类型过滤 | 可选择只检查 schematic、layout 或其他特定 view |
| 性能优化 | 使用 instanceMasters 而非 instances,自动去重 |
| 双输出模式 | 控制台输出 + 文件报告两种模式 |
| 详细统计 | 提供扫描统计和外部引用计数 |
核心技术
基于 Cadence 官方文档验证
本工具的所有关键函数调用都经过 Virtuoso Studio Design Environment SKILL Reference IC25.1 官方文档验证:
| 函数/属性 | 用途 | 验证状态 |
|---|---|---|
ddGetObj(libName) |
获取库对象 | ✅ 正确 |
libId~>cells |
遍历库中的 cells | ✅ 正确 |
cellId~>views |
遍历 cell 中的 views | ✅ 正确 |
dbOpenCellViewByType() |
以只读模式打开 cellview | ✅ 正确 |
cv~>instanceMasters |
获取唯一 masters(性能优化) | ⭐ 最佳实践 |
master~>libName / cellName |
获取 master 属性 | ✅ 正确 |
dbClose(cv) |
释放内存 | ✅ 正确 |
性能优化亮点
使用 cv~>instanceMasters 而非 cv~>instances
; ✅ 高效方法(本工具使用)
masters = cv~>instanceMasters ; 自动去重,每个 master 只出现一次
foreach(master masters
masterLib = master~>libName
; 处理一次即可
)
; ❌ 低效方法(不推荐)
foreach(inst cv~>instances ; 包含所有实例,重复的 master
masterLib = inst~>master~>libName
; 同一个 master 可能被处理多次
)
性能对比:
- 对于有 1000 个实例但只有 50 个不同 master 的设计
instanceMasters只需处理 50 次instances需要处理 1000 次- 性能提升 20 倍!
核心算法
procedure(CCSFindExternalRefs(libListA libListB @key (viewFilter list("schematic" "layout")))
let((libId cv masters externalCount)
externalCount = 0
; 遍历待扫描的库列表 A
foreach(libName libListA
libId = ddGetObj(libName)
when(libId
; 遍历库中所有 cell
foreach(cellId libId~>cells
; 遍历 cell 中的 view
foreach(viewId cellId~>views
viewName = viewId~>name
; 只处理指定的 view 类型
when(member(viewName viewFilter)
; 以只读模式打开 cellview
cv = dbOpenCellViewByType(libName cellId~>name viewName "" "r")
when(cv
; 获取所有唯一的 instance masters
masters = cv~>instanceMasters
; 检查每个 master
foreach(master masters
masterLib = master~>libName
masterCell = master~>cellName
; 检查 master 库是否在允许列表 B 中
unless(member(masterLib libListB)
; 找到外部引用,报告
printf(" [EXTERNAL REF] %s/%s/%s -> %s/%s\n"
libName cellId~>name viewName masterLib masterCell)
externalCount = externalCount + 1
)
)
; 关闭 cellview 释放内存
dbClose(cv)
)
)
)
)
)
)
externalCount ; 返回外部引用计数
)
)
使用方法
1. 加载脚本
load("CCSFindExternalRefs.il")
2. 基本用法(控制台输出)
; 定义库列表
libListA = list("MY_IP_LIB" "MY_DESIGN_LIB")
libListB = list("BASIC_LIB" "STD_CELL_LIB" "MY_IP_LIB" "MY_DESIGN_LIB")
; 运行检查
CCSFindExternalRefs(libListA libListB)
输出示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
=============================================================
External Library Reference Checker
=============================================================
Scanning Libraries: ("MY_IP_LIB" "MY_DESIGN_LIB")
Allowed Ref Libs: ("BASIC_LIB" "STD_CELL_LIB" "MY_IP_LIB" "MY_DESIGN_LIB")
View Filter: ("schematic" "layout")
=============================================================
--- Processing Library: MY_IP_LIB ---
[EXTERNAL REF] MY_IP_LIB/AMPLIFIER/schematic -> VENDOR_LIB/NMOS_33
[EXTERNAL REF] MY_IP_LIB/AMPLIFIER/schematic -> VENDOR_LIB/PMOS_33
--- Processing Library: MY_DESIGN_LIB ---
[EXTERNAL REF] MY_DESIGN_LIB/TOP/schematic -> OLD_LIB/LEGACY_BLOCK
=============================================================
Summary:
Total Cells Scanned: 45
Total Views: 128
Checked Views: 90 (filter: ("schematic" "layout"))
External References Found: 3
=============================================================
3. 高级用法
带详细输出
CCSFindExternalRefs(libListA libListB ?verbose t)
只检查特定 View
; 只检查 schematic
CCSFindExternalRefs(libListA libListB ?viewFilter list("schematic"))
; 只检查 layout
CCSFindExternalRefs(libListA libListB ?viewFilter list("layout"))
; 检查多种 view
CCSFindExternalRefs(libListA libListB ?viewFilter list("schematic" "layout" "config"))
生成报告到文件
CCSFindExternalRefsToFile(
libListA
libListB
"external_refs_report.txt"
)
报告文件示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
External Library Reference Report
Generated: Mon Jan 26 17:58:00 2026
=============================================================
Scanning Libraries: ("MY_IP_LIB" "MY_DESIGN_LIB")
Allowed Ref Libs: ("BASIC_LIB" "STD_CELL_LIB" "MY_IP_LIB" "MY_DESIGN_LIB")
Total External References Found: 3
=============================================================
Library Cell View -> Ref Library Ref Cell
------------------------------------------------------------------------------------------------------------------------
MY_IP_LIB AMPLIFIER schematic -> VENDOR_LIB NMOS_33
MY_IP_LIB AMPLIFIER schematic -> VENDOR_LIB PMOS_33
MY_DESIGN_LIB TOP schematic -> OLD_LIB LEGACY_BLOCK
实际应用案例
案例 1:IP 交付前检查
; IP 库只应该引用标准工艺库
ipLibs = list("MY_IP_CORE" "MY_IP_IO")
allowedLibs = list(
"TSMC28_BASIC" ; 工艺库
"TSMC28_IO" ; IO 库
"MY_IP_CORE" ; IP 自己
"MY_IP_IO" ; IP 自己
)
; 检查是否有非法引用
refCount = CCSFindExternalRefs(ipLibs allowedLibs)
if(refCount > 0 then
printf("WARNING: Found %d external references!\n" refCount)
printf("Please fix before IP delivery!\n")
else
printf("PASS: No external references found.\n")
)
案例 2:项目依赖审计
; 审计整个项目的依赖关系
projectLibs = list(
"TOP_DESIGN"
"SUB_BLOCK_A"
"SUB_BLOCK_B"
"TESTBENCH"
)
allowedLibs = append(
projectLibs ; 项目自己的库
list(
"STD_CELL_LIB"
"MEMORY_COMPILER"
"IO_LIB"
)
)
; 生成详细报告
CCSFindExternalRefsToFile(
projectLibs
allowedLibs
"project_dependency_audit.txt"
)
案例 3:逐步引用分析
; 分析多层依赖关系
; 第一层:只允许项目内部引用
internalLibs = list("LIB_A" "LIB_B" "LIB_C")
printf("\n=== Level 1: Internal Dependencies ===\n")
CCSFindExternalRefs(internalLibs internalLibs)
; 第二层:允许基础库
allowedLevel2 = append(internalLibs list("BASIC_LIB"))
printf("\n=== Level 2: With Basic Library ===\n")
CCSFindExternalRefs(internalLibs allowedLevel2)
; 第三层:允许所有标准库
allowedLevel3 = append(allowedLevel2 list("STD_CELL" "IO_LIB"))
printf("\n=== Level 3: With All Standard Libraries ===\n")
CCSFindExternalRefs(internalLibs allowedLevel3)
参数说明
CCSFindExternalRefs 函数
CCSFindExternalRefs(
libListA ; 待扫描的库列表
libListB ; 允许引用的库列表
@key
(viewFilter list("schematic" "layout")) ; 要检查的 view 类型
(verbose nil) ; 是否显示详细输出
)
=> 外部引用数量
| 参数 | 类型 | 必需 | 默认值 | 说明 |
|---|---|---|---|---|
libListA |
list | ✅ | - | 待扫描的库列表 |
libListB |
list | ✅ | - | 允许引用的库列表 |
viewFilter |
list | ❌ | ("schematic" "layout") |
要检查的 view 类型 |
verbose |
boolean | ❌ | nil |
是否显示详细输出 |
CCSFindExternalRefsToFile 函数
CCSFindExternalRefsToFile(
libListA ; 待扫描的库列表
libListB ; 允许引用的库列表
outputFile ; 输出报告文件路径
@key
(viewFilter list("schematic" "layout"))
)
=> 外部引用数量
注意事项与最佳实践
[!WARNING] 重要提醒
- 库名比较是大小写敏感的:
"MyLib"≠"mylib"- 确保库名与
cds.lib中完全一致- 对于大型库扫描,建议使用文件输出模式
[!TIP] 最佳实践
- View 过滤:默认只检查
schematic和layout,跳过symbol等辅助 view- 内存管理:脚本自动调用
dbClose释放资源,适合大规模扫描- 只读模式:使用
"r"模式打开,保证不会意外修改设计- 分批扫描:对于超大型库,可以分批扫描减少内存压力
性能建议
预期性能
基于 cv~>instanceMasters 的优化实现:
| 库规模 | 预期时间 |
|---|---|
| 小型库(< 100 cells) | 几秒钟 |
| 中型库(100-1000 cells) | 1-5 分钟 |
| 大型库(> 1000 cells) | 5-30 分钟 |
优化建议
- 使用文件输出版本 - 对于大型库
CCSFindExternalRefsToFile(...) ; 而不是 CCSFindExternalRefs - 限制 View 类型 - 只检查必要的 view
?viewFilter list("schematic") ; 只检查 schematic - 分批扫描 - 分批处理而不是一次全部
CCSFindExternalRefs(list("LIB_A") allowedLibs) CCSFindExternalRefs(list("LIB_B") allowedLibs)
故障排查
问题 1:库未找到
症状:
1
[ERROR] Library not found in cds.lib: XXX_LIB
解决方法:
; 检查库名拼写和大小写
ddGetObj("XXX_LIB") ; 返回 nil 说明库不存在
; 确认库已在 cds.lib 中定义
问题 2:无法打开 cellview
症状:
1
[WARNING] Could not open cellview: LIB/CELL/VIEW
可能原因:
- Cellview 文件损坏
- 正被其他进程占用
- 文件权限问题
解决方法:
; 使用 ?verbose t 查看具体哪个 cellview
CCSFindExternalRefs(libListA libListB ?verbose t)
; 在 Library Manager 中手动尝试打开该 cellview
问题 3:结果为 0 但预期有外部引用
可能原因:
viewFilter设置不正确libListB包含了所有引用的库
解决方法:
; 检查 viewFilter
?viewFilter list("schematic" "layout" "config")
; 检查 libListB 是否过于宽泛
扩展功能建议
1. 生成 HTML 报告
修改输出格式,生成更易读的 HTML 报告:
fp = outfile("report.html")
fprintf(fp "<html><body><table>...")
2. 按引用库分组
统计每个外部库被引用的次数:
; 使用 hash table 统计
libRefCount = makeTable("libRefCount")
libRefCount[masterLib] = libRefCount[masterLib] + 1
3. 递归依赖检查
不仅检查直接引用,还检查引用库的引用(多层依赖)
4. 与 CI/CD 集成
在版本控制系统的 pre-commit hook 中运行,自动检查非法依赖
完整代码
完整的 CCSFindExternalRefs.il 脚本代码(可直接复制使用):
;;--------------------------------------------------------------------
;; File : CCSFindExternalRefs.il
;; Purpose : 查找库列表A中引用了非库列表B的所有实例
;; Author : Ben
;; Created : 2026/01/26
;;--------------------------------------------------------------------
;;
;; 功能说明:
;; 本脚本遍历指定的库列表A(待扫描的库),检查其中所有 cellview 的实例引用,
;; 找出引用了不在库列表B(允许引用的库)中的外部依赖。
;;
;; 核心逻辑:
;; 1. 遍历库列表A中的每个库
;; 2. 遍历每个库中的所有 cell
;; 3. 遍历每个 cell 中的所有 view(通常是 schematic 和 layout)
;; 4. 打开 cellview,获取其中的所有 instance masters
;; 5. 检查每个 master 的库名是否不在库列表B中
;; 6. 如果不在,报告这个外部引用
;;
;; 使用示例:
;; libListA = list("MY_LIB" "TEST_LIB")
;; libListB = list("BASIC_LIB" "IP_LIB" "MY_LIB" "TEST_LIB")
;; CCSFindExternalRefs(libListA libListB)
;;--------------------------------------------------------------------
procedure(CCSFindExternalRefs(libListA libListB @key (viewFilter list("schematic" "layout")) (verbose nil))
let((libId cv masters externalCount totalCells totalViews checkedViews)
externalCount = 0
totalCells = 0
totalViews = 0
checkedViews = 0
printf("\n=============================================================\n")
printf("External Library Reference Checker\n")
printf("=============================================================\n")
printf("Scanning Libraries: %L\n" libListA)
printf("Allowed Ref Libs: %L\n" libListB)
printf("View Filter: %L\n" viewFilter)
printf("=============================================================\n\n")
;; 遍历库列表A
foreach(libName libListA
printf("\n--- Processing Library: %s ---\n" libName)
;; 获取库对象
libId = ddGetObj(libName)
if(libId then
;; 遍历库中所有 cell
foreach(cellId libId~>cells
cellName = cellId~>name
totalCells = totalCells + 1
when(verbose
printf(" Checking Cell: %s\n" cellName)
)
;; 遍历 cell 中的所有 view
foreach(viewId cellId~>views
viewName = viewId~>name
totalViews = totalViews + 1
;; 只处理指定的 view 类型(如 schematic, layout)
when(member(viewName viewFilter)
checkedViews = checkedViews + 1
when(verbose
printf(" Opening Cellview: %s/%s/%s\n" libName cellName viewName)
)
;; 以只读模式打开 cellview
cv = dbOpenCellViewByType(libName cellName viewName "" "r")
if(cv then
;; 获取所有唯一的 instance masters(比遍历所有实例效率高)
masters = cv~>instanceMasters
;; 检查每个 master
foreach(master masters
masterLib = master~>libName
masterCell = master~>cellName
;; 检查 master 所属库是否在允许列表B中
unless(member(masterLib libListB)
;; 找到外部引用,报告
printf(" [EXTERNAL REF] %s/%s/%s -> %s/%s\n"
libName cellName viewName masterLib masterCell)
externalCount = externalCount + 1
)
)
;; 关闭 cellview
dbClose(cv)
else
;; 无法打开 cellview
when(verbose
printf(" [WARNING] Could not open cellview: %s/%s/%s\n"
libName cellName viewName)
)
)
) ; when member viewFilter
) ; foreach viewId
) ; foreach cellId
else
;; 库不存在
printf(" [ERROR] Library not found in cds.lib: %s\n" libName)
)
) ; foreach libName
;; 打印统计信息
printf("\n=============================================================\n")
printf("Summary:\n")
printf(" Total Cells Scanned: %d\n" totalCells)
printf(" Total Views: %d\n" totalViews)
printf(" Checked Views: %d (filter: %L)\n" checkedViews viewFilter)
printf(" External References Found: %d\n" externalCount)
printf("=============================================================\n\n")
;; 返回外部引用计数
externalCount
)
)
;;--------------------------------------------------------------------
;; 增强版本:生成详细报告到文件
;;--------------------------------------------------------------------
procedure(CCSFindExternalRefsToFile(libListA libListB outputFile @key (viewFilter list("schematic" "layout")))
let((libId cv masters externalCount totalCells fp externalRefs)
externalCount = 0
totalCells = 0
externalRefs = nil ; 存储所有外部引用信息
printf("\n=============================================================\n")
printf("External Library Reference Checker (File Output)\n")
printf("=============================================================\n")
printf("Scanning Libraries: %L\n" libListA)
printf("Allowed Ref Libs: %L\n" libListB)
printf("Output File: %s\n" outputFile)
printf("=============================================================\n\n")
;; 遍历库列表A
foreach(libName libListA
printf("Processing Library: %s\n" libName)
libId = ddGetObj(libName)
when(libId
foreach(cellId libId~>cells
cellName = cellId~>name
totalCells = totalCells + 1
foreach(viewId cellId~>views
viewName = viewId~>name
when(member(viewName viewFilter)
cv = dbOpenCellViewByType(libName cellName viewName "" "r")
when(cv
masters = cv~>instanceMasters
foreach(master masters
masterLib = master~>libName
masterCell = master~>cellName
unless(member(masterLib libListB)
;; 记录外部引用信息
externalRefs = cons(
list(
libName ; 源库
cellName ; 源 cell
viewName ; 源 view
masterLib ; 引用的库
masterCell ; 引用的 cell
)
externalRefs
)
externalCount = externalCount + 1
)
)
dbClose(cv)
)
)
)
)
)
)
;; 反转列表以保持顺序
externalRefs = reverse(externalRefs)
;; 写入文件
fp = outfile(outputFile)
if(fp then
fprintf(fp "External Library Reference Report\n")
fprintf(fp "Generated: %s\n" getCurrentTime())
fprintf(fp "=============================================================\n\n")
fprintf(fp "Scanning Libraries: %L\n" libListA)
fprintf(fp "Allowed Ref Libs: %L\n\n" libListB)
fprintf(fp "Total External References Found: %d\n\n" externalCount)
fprintf(fp "=============================================================\n\n")
fprintf(fp "%-20s %-20s %-15s -> %-20s %-20s\n"
"Library" "Cell" "View" "Ref Library" "Ref Cell")
fprintf(fp "%s\n" makePaddedString("-" 120))
;; 写入每个外部引用
foreach(ref externalRefs
fprintf(fp "%-20s %-20s %-15s -> %-20s %-20s\n"
car(ref) ; libName
cadr(ref) ; cellName
caddr(ref) ; viewName
cadddr(ref) ; masterLib
car(cddddr(ref)) ; masterCell
)
)
close(fp)
printf("\nReport written to: %s\n" outputFile)
else
error("Cannot create output file: %s" outputFile)
)
printf("Total External References Found: %d\n\n" externalCount)
externalCount
)
)
;; 辅助函数:创建填充字符串
procedure(makePaddedString(char count)
let((result)
result = ""
for(i 1 count
result = strcat(result char)
)
result
)
)
;;--------------------------------------------------------------------
;; 使用示例
;;--------------------------------------------------------------------
;;
;; 1. 基本用法(控制台输出):
;; libListA = list("MY_LIB" "TEST_LIB")
;; libListB = list("BASIC_LIB" "IP_LIB" "MY_LIB" "TEST_LIB")
;; CCSFindExternalRefs(libListA libListB)
;;
;; 2. 带详细输出:
;; CCSFindExternalRefs(libListA libListB ?verbose t)
;;
;; 3. 只检查特定 view:
;; CCSFindExternalRefs(libListA libListB ?viewFilter list("schematic"))
;;
;; 4. 生成详细报告到文件:
;; CCSFindExternalRefsToFile(
;; libListA
;; libListB
;; "external_refs_report.txt"
;; )
;;
;; 注意事项:
;; - viewFilter 默认只检查 schematic 和 layout view
;; - 使用 cv~>instanceMasters 比 cv~>instances 效率更高(自动去重)
;; - 所有库名比较都是大小写敏感的
;; - 建议在扫描大型库时使用文件输出版本
;;--------------------------------------------------------------------
官方文档验证
本工具所有关键函数都经过 Cadence Support 官方文档验证:
- ✅ ddGetObj - Virtuoso Studio Design Environment SKILL Reference
- ✅ dbOpenCellViewByType - 正确的参数和只读模式
- ✅ cv~>instanceMasters - 官方推荐的性能优化方法
- ✅ dbClose - 必需的内存管理
详细验证报告请参考项目文档。
总结
CCSFindExternalRefs.il 提供了一套完整的库依赖关系检查解决方案:
- ✅ 准确可靠:基于 Cadence 官方 SKILL Reference,所有函数调用正确
- ✅ 性能优化:使用
instanceMasters自动去重,比遍历所有实例快数倍 - ✅ 灵活配置:支持自定义库列表和 view 类型过滤
- ✅ 双输出模式:控制台 + 文件报告
- ✅ 内存安全:自动
dbClose,适合大规模扫描 - ✅ 易于扩展:清晰的代码结构,便于添加新功能
适用场景:
- IP 交付前的依赖关系检查
- 项目级依赖关系审计
- 库迁移准备和规划
- 设计规范合规性检查
合理使用该工具,可以大大提高 IC 设计中库管理和依赖关系控制的效率,确保设计的完整性和可移植性。