在做电路迁移或库整理时,经常需要把一个库下所有 cell 的引脚批量改为 inputoutput。 逐个 cell 手动改既费时又容易遗漏,用 SKILL 脚本在 CIW 中批量处理是最高效的方式。

本文整理自 Cadence ASK 三篇技术文章,完整覆盖 schematic + symbol 双视图,以及改方向时必须注意的 Floating net 问题。


一、背景:两个视图、两层属性

Virtuoso 中一个 cell 通常同时拥有 schematic 视图和 symbol 视图,两者都独立持有引脚信息:

视图 引脚属性 引脚视觉符号
schematic term~>direction basic/ipinbasic/opinbasic/iopin
symbol term~>direction 同上(symbol 自己的 pin instance)

两视图的方向必须一致,否则 Check and Save 会报:

1
Warning: Terminal "A" in view schematic is (inputOutput) but is (input) in "cellName symbol".

另一个陷阱:在 schematic 里直接改方向时,pin 的视觉符号(三角形)位置会偏移,导致与之相连的导线断开,产生 Floating net 错误。


二、三种策略选择

策略 适用场景 是否保留 Symbol 自定义外观
A. 仅改 schematic,重新生成 symbol Symbol 无自定义布局,可以整体重建 否,symbol 完全重建
B. 分别改 schematic 和 symbol 的 direction 属性 需保留 symbol 外观,只同步电气属性
C. 先改 symbol,再用 CCSUpdatePinDir 同步 schematic 最稳健,自动处理 Floating net

三、策略 A:改 schematic → 重建 symbol

适用于 symbol 没有手工定制、可以完全从 schematic 重新生成的场景。

步骤 1:批量修改 schematic 引脚方向

将以下脚本保存为 CCSchgPinDir.il,在 CIW 执行 load "CCSchgPinDir.il"

procedure(CCSchgPinDir(sourceLibName viewName pinList pinDir)
let((cv cellId pin_id)
   foreach(sourceCellName ddGetObj(sourceLibName)~>cells~>name
         cellId = ddGetObj(sourceLibName sourceCellName)
                foreach(view cellId~>views~>name
                        if(view == viewName
                        then
                        cv = dbOpenCellViewByType(sourceLibName sourceCellName view "" "a")
                        foreach(pin pinList
                                pin_id = dbFindTermByName(cv pin)
                                if(pin_id
                                then
                                printf("found pin %L in cell %L \n" pin sourceCellName)
                                pin_id~>direction = pinDir
                                else
                                printf("pin %L does not exist in cell %L \n" pin sourceCellName)
                                ) ;if
                                ) ;foreach pin
                        dbSave(cv)
                        dbClose(cv)
                        ) ;if
                        ) ;foreach view
        ) ;foreach cell
  ) ;let
) ;procedure

在 CIW 调用:

1
2
; 将 mylib 所有 schematic 中名为 A、Z、ZX 的引脚改为 input
CCSchgPinDir("mylib" "schematic" list("A" "Z" "ZX") "input")

步骤 2:从 schematic 重建整库 symbol

将以下脚本保存为 CCSautoCreateSymbol.il,在 CIW 执行:

procedure(CCSautoCreateSymbol(sourceLib reCreate)
   let((libId targetLib cellList sourceView targetView funct1 funct2)
      libId      = ddGetObj(sourceLib)
      targetLib  = sourceLib
      cellList   = libId~>cells
      sourceView = "schematic"
      targetView = "symbol"
      funct1     = "schSchemToPinList"
      funct2     = "schPinListToSymbol"
      foreach(cell cellList
         if(member(sourceView cell~>views~>name) then
            if(reCreate || !member("symbol" cell~>views~>name) then
               hiRegTimer("hiDBoxUser(schReplaceOrModifyDialog nil 1)" 5)
               hiRegTimer("hiDBoxOK(cvToCvCreateCDF)" 10)
               schViewToView(sourceLib cell~>name targetLib cell~>name
                             sourceView targetView funct1 funct2)
            ) ;if
         ) ;if
      ) ;foreach
      ddReleaseObj(libId)
      printf("\nJob Done!\n")
   ) ;let
) ;procedure

在 CIW 调用:

1
2
3
4
5
6
7
load "CCSautoCreateSymbol.il"

; 重建整库所有 symbol(覆盖已有 symbol)
CCSautoCreateSymbol("myLib" t)

; 仅为还没有 symbol 的 cell 新建(已有 symbol 的不动)
CCSautoCreateSymbol("myLib" nil)

注意reCreate=t完全删除并重建已有 symbol,自定义的图形外观、端口位置均会丢失。


四、策略 B:分别改 schematic 和 symbol 的 direction 属性

如果 symbol 有精心设计的布局,不希望被重建,可以只同步逻辑方向属性(term~>direction),不改图形 master。 用上面的 CCSchgPinDir 对两个视图分别调用即可:

1
2
3
4
5
; schematic 视图
CCSchgPinDir("mylib" "schematic" list("A" "Z" "ZX") "input")

; symbol 视图(同一函数,只改 viewName 参数)
CCSchgPinDir("mylib" "symbol" list("A" "Z" "ZX") "input")

结果:电气方向属性同步,Check and Save 不再报 mismatch 警告;但 symbol 上三角箭头的朝向(ipin/opin 图形)不变。 若要同时替换图形,针对单个 cell 用下面的函数:

procedure(CCSchgPinDirMaster(sourceLibName sourceCellName pinList newDirection)
let((cv cv_sym pin_id pin_cell)
case(newDirection
    ("input"       pin_cell="ipin")
    ("output"      pin_cell="opin")
    ("inputOutput" pin_cell="iopin")
    (t error("Invalid direction %L\n" newDirection))
    )
cv_sym = dbOpenCellViewByType("basic" pin_cell "symbol" "schematicSymbol" "r")
cv     = dbOpenCellViewByType(sourceLibName sourceCellName "schematic" "schematic" "a")
foreach(pin pinList
        pin_id = dbFindTermByName(cv pin)
        if(pin_id
            then
            pin_id~>direction = newDirection
            foreach(pin1 pin_id~>pins
                pin1~>fig~>master = cv_sym
                )
           )
        )
dbSave(cv)
dbClose(cv)
) ;let
) ;procedure

五、策略 C:先改 symbol,再同步 schematic,自动处理 Floating net

核心优势:CCSUpdatePinDir 在改方向时会自动检测并重建与 pin 相连的导线,彻底避免 Floating net 报错。 此脚本来自 Cadence ASK 文章 a1Od0000000nVZLEA2,专门用于解决 schematic/symbol 方向不一致及 Floating net 的问题。

步骤 1:修改 symbol 引脚方向

若只改指定引脚,用 CCSchgPinDir

1
CCSchgPinDir("mylib" "symbol" list("A" "Z" "ZX") "input")

若要遍历整库所有 cell 的所有引脚(无需提前知道名称),用以下脚本:

注意:symbol 视图中 pin~>fig 返回的是 shape/polygon 对象(图形),不是 instance,不能对其赋值 master,否则报 dbSetq: Invalid type
正确做法是只改 ter~>direction;若同时需要更新视觉箭头,用 schPinListToSymbol() 重建 pin 表达(见方式 2)。

方式 1:只改 direction 属性(保留 symbol 外观,视觉箭头不变)

procedure(CCSchangeAllSymbolPinDir(libName newDirection)
let((cv)
  foreach(cellName ddGetObj(libName)~>cells~>name
    when(ddGetObj(libName cellName "symbol")
      cv = dbOpenCellViewByType(libName cellName "symbol" "" "a")
      foreach(ter cv~>terminals
        ter~>direction = newDirection
        printf("Set %L/%L pin %L -> %L\n" libName cellName ter~>name newDirection)
      )
      dbSave(cv)
      dbClose(cv)
    )
  )
)
)

方式 2:同时更新视觉箭头(用 schPinListToSymbol 重建 pin 表示)

procedure(CCSchangeAllSymbolPinDirWithMaster(libName newDirection)
let((cv pinList)
  foreach(cellName ddGetObj(libName)~>cells~>name
    when(ddGetObj(libName cellName "symbol")
      cv = dbOpenCellViewByType(libName cellName "symbol" "schematicSymbol" "r")
      pinList = foreach(mapcar ter cv~>terminals
        `(nil name ,(ter~>name) direction ,newDirection)
      )
      dbClose(cv)
      schPinListToSymbol(libName cellName "symbol"
                         `(nil ports ,pinList))
      printf("Updated %L/%L all pins -> %L\n" libName cellName newDirection)
    )
  )
)
)

在 CIW 调用:

1
2
3
4
5
6
7
load "CCSchangeAllSymbolPinDir.il"

; 方式 1:只改电气属性,不动外观
CCSchangeAllSymbolPinDir("mylib" "inputOutput")

; 方式 2:同时更新视觉箭头(会重建 pin 布局)
CCSchangeAllSymbolPinDirWithMaster("mylib" "inputOutput")

步骤 2:让 schematic 自动跟随 symbol,保留连线

将以下脚本保存为 CCSUpdatePinDir.il,在 CIW 执行 load "CCSUpdatePinDir.il"

procedure(CCSUpdatePinDir(libName @optional (cellList '()))
let((cv cv1 symTer)
   if(!cellList then
      cellList = ddGetObj(libName)~>cells~>name
     )
   foreach(cell cellList
     when(and(ddGetObj(libName cell "schematic") ddGetObj(libName cell "symbol"))
       cv  = dbOpenCellViewByType(libName cell "schematic" "schematic" "a")
       cv1 = dbOpenCellViewByType(libName cell "symbol")
       foreach(ter cv~>terminals
         symTer = car(setof(x cv1~>terminals x~>name==ter~>name))
         if(ter~>direction != symTer~>direction then
            CCSchg_pin_dir(cv list(ter~>name) symTer~>direction)
           )
       ) ;foreach ter
       dbSave(cv)
       dbClose(cv)
       dbClose(cv1)
     ) ;when
   ) ;foreach
   printf("\nDone ...")
) ;let
) ;procedure

; 内部函数:改方向 + 换 master + 重建连线(避免 Floating net)
procedure(CCSchg_pin_dir(cv pinList newDirection)
let((pin_id pin_cell newMaster pts myBBox myWire myPt)
case(newDirection
     ("input"       pin_cell="ipin")
     ("output"      pin_cell="opin")
     ("inputOutput" pin_cell="iopin")
     (t error("Invalid direction %L\n" newDirection))
     )
newMaster = dbOpenCellViewByType("basic" pin_cell "symbol" "schematicSymbol" "r")
foreach(pin1 pinList
  pin_id = setof(x cv~>instances and(x~>pin x~>net~>name==pin1))
  if(pin_id then
    foreach(pin pin_id
      myBBox = CCSPinBox(pin)
      myWire = car(setof(x dbGetOverlaps(pin~>cellView myBBox)
                    x~>objType == "line"))
      when(myWire
        pts = myWire~>points
        schDelete(myWire)
        foreach(point pts
          if(if(dbGetDatabaseType()=="CDBA"
                then absIsPointInsideFig(pin point)
                else leIsPointInsideFig(pin point))
          then nil
          else myPt = point)
        )
        pin~>pin~>term~>direction = newDirection
        pin~>master = newMaster
        schCreateWire(pin~>cellView "route" "full"
                      list(myPt centerBox(CCSPinBox(pin)))
                      0.0625 0.0625 0.0)
      ) ;when
    ) ;foreach pin
  ) ;if
) ;foreach pin1
schCheck(cv)
) ;let
) ;procedure

procedure(CCSPinBox(inst)
let((pinFig newBox)
pinFig = car(setof(x car(dbGetTrueOverlaps(inst~>cellView inst~>bBox
                          list("pin" "drawing") 1))
              x~>objType=="polygon"))
newBox = dbTransformBBox(pinFig~>bBox inst~>transform)
newBox
) ;let
) ;procedure

在 CIW 调用:

1
2
3
4
5
6
7
load "CCSUpdatePinDir.il"

; 更新整库所有 cell(schematic 方向同步到 symbol)
CCSUpdatePinDir("myLib")

; 只更新指定 cell
CCSUpdatePinDir("myLib" '("Cell1" "Cell_A"))

六、完整推荐工作流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
;; === 策略 C 完整流程举例(遍历整库所有引脚,无需指定引脚名)===

;; 1. 先改整库所有 symbol 视图的所有引脚方向(direction + master 一起改)改为input
load "CCSchangeAllSymbolPinDir.il"
CCSchangeAllSymbolPinDir("mylib" "input")

;; 2. 让 schematic 自动跟随 symbol 方向,且重建导线避免 Floating net
load "CCSUpdatePinDir.il"
CCSUpdatePinDir("mylib")

;; === 若 symbol 可以整体重建(策略 A)举例===

;; 1. 先改整库所有 schematic 视图的所有引脚方向(遍历,无需指定引脚名)
load "CCSchangeAllSymbolPinDir.il"   ; 函数同样适用于 schematic 视图,改 viewName 即可
; 或直接在 CIW 执行:
foreach(cellName ddGetObj("mylib")~>cells~>name
  when(ddGetObj("mylib" cellName "schematic")
    cv = dbOpenCellViewByType("mylib" cellName "schematic" "" "a")
    foreach(ter cv~>terminals ter~>direction = "input")
    dbSave(cv) dbClose(cv)
  )
)

;; 2. 从 schematic 重建整库 symbol
load "CCSautoCreateSymbol.il"
CCSautoCreateSymbol("mylib" t)   ; t = 覆盖已有 symbol

七、关键 SKILL API 速查

API 作用
ddGetObj(lib)~>cells~>name 获取库下所有 cell 名称列表
dbOpenCellViewByType(lib cell view type "a") 以 append 模式打开视图(可写)
cv~>terminals 视图中所有逻辑引脚(term)
dbFindTermByName(cv name) 按名称查找 term
term~>direction = "input" 修改逻辑方向属性
pin~>fig~>master = cv_sym 替换引脚图形 master(ipin/opin/iopin)
schViewToView(...) 从 schematic 生成 symbol(重建)
schCreateWire(...) 创建导线(用于重建断开的连线)
dbSave(cv) / dbClose(cv) 保存并关闭

八、注意事项

  • 备份优先:所有脚本以 append 模式直接写磁盘,操作前务必备份库目录。
  • 策略 A(重建 symbol)会丢失自定义外观:如果 symbol 有手工调整的布局或注释,用策略 B/C。
  • Floating net 根因:直接修改 schematic 中 term~>direction 时,pin 的 bounding box 会随 master 改变而偏移,导致连线端点落在 pin 框外而断开。策略 C 的 CCSchg_pin_dir 通过”删线→改 pin→重建线”的顺序解决这个问题。
  • pinDir 合法值:只有 "input""output""inputOutput"(驼峰,区分大小写)。
  • ssgSortPins 环境变量:重建 symbol 时,引脚在边框上的排布顺序受此变量控制("alphanumeric""geometric"),可在 CIW 执行 schSetEnv("ssgSortPins" "geometric") 修改。