在 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 过滤:默认只检查 schematiclayout,跳过 symbol 等辅助 view
  • 内存管理:脚本自动调用 dbClose 释放资源,适合大规模扫描
  • 只读模式:使用 "r" 模式打开,保证不会意外修改设计
  • 分批扫描:对于超大型库,可以分批扫描减少内存压力

性能建议

预期性能

基于 cv~>instanceMasters 的优化实现:

库规模 预期时间
小型库(< 100 cells) 几秒钟
中型库(100-1000 cells) 1-5 分钟
大型库(> 1000 cells) 5-30 分钟

优化建议

  1. 使用文件输出版本 - 对于大型库
    CCSFindExternalRefsToFile(...)  ; 而不是 CCSFindExternalRefs
    
  2. 限制 View 类型 - 只检查必要的 view
    ?viewFilter list("schematic")  ; 只检查 schematic
    
  3. 分批扫描 - 分批处理而不是一次全部
    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 设计中库管理和依赖关系控制的效率,确保设计的完整性和可移植性。

参考资料

  1. Virtuoso Studio Design Environment SKILL Reference IC25.1
  2. Cadence Design Framework II User Guide
  3. OpenAccess 2.2 API Documentation