共计 2514 个字符,预计需要花费 7 分钟才能阅读完成。
手动布局的三大痛点
作为 IC 设计新手,手动布局就像用绣花针搭建乐高城堡——我经历过连续 48 小时调整 MOS 管间距却因漏掉一个接触孔导致流片失败的惨痛教训。主要问题集中在:

- 时间黑洞:一个中等规模模块的器件摆放需要反复拖动、对齐、验证,占项目 60% 以上时间
- 人为错误 :设计规则检查(DRC) 阶段常发现间距 / 包围等基础错误,90% 源自手动操作疏忽
- 版本灾难:每次工艺更新或模块调整,所有手动布局需推倒重来
自动化方案选型:Python 还是 Skill?
在 Cadence 设计环境中,两种主流自动化方案各有利弊:
- Python 方案
- 优势:生态丰富,可用 pya 等库处理复杂算法
-
劣势:通过 API 调用存在性能损耗,无法直接操作数据库
-
Skill 方案
- 优势:原生支持 Virtuoso 数据库操作,执行效率高
- 劣势:学习曲线陡峭,调试工具较少
新手推荐路径:先用 Skill 实现基础布局自动化,再通过 Python 调用处理算法密集型任务
Skill 脚本速成课(含生存必备语法)
基础语法三件套
; 变量定义(注意动态类型)
let((x y)
x = 5 ; 整数
y = "cell" ; 字符串
list('(1 2 3)) ; 列表
)
; 循环结构
foreach(i '(1 2 3 4 5)
printf("Value: %d\n" i)
)
; 函数定义
defun(createRect (@optional (layer "M1") (width 0.1))
geCreateRect(
?layer layer
?width width
)
)
版图元素操作实战
这段代码演示如何自动放置标准单元并连线:
procedure(autoPlaceInstance()
let((lib cell view inst bBox)
; 打开目标库
lib = ddGetObj("analogLib")
cell = dbOpenCellViewByType(lib "nmos" "symbol" """r")
; 在 (100,200) 坐标放置实例
inst = dbCreateInst(geGetEditCellView()
cell "I1"
list(100:200) "R0"
)
; 获取边界框用于连线
bBox = dbGetInstBBox(inst)
; 自动创建 M1 层金属连线
geCreatePath(
?layer "M1"
?width 0.2
?pts list(list(xCoord(bBox->lowerLeft) yCoord(bBox->upperRight))
list(xCoord(bBox->upperRight)+5 yCoord(bBox->upperRight))
)
)
)
)
DRC 自动化实现技巧
通过内置 drcQuery 函数实现间距检查自动化:
procedure(checkPolySpace(@optional (minSpace 0.15))
let((violations)
violations = drcQuery(
?rule "space"
?layer1 "poly"
?value minSpace
?cell geGetEditCellView())
when(violations
foreach(viol violations
printf("Violation at %L\n" viol->coordinates)
)
)
)
)
性能优化双引擎
内存管理黄金法则
- 对象回收 :用
dbRelease释放不再使用的版图对象 - 批处理:超过 1000 个实例时,分区块处理并定期调用
garbageCollect
多线程加速方案
; 启动多线程任务
defun(parallelPlace(instList)
let((workers)
workers = makeThreadPool(4) ; 创建 4 线程池
foreach(inst instList
threadPoolSubmit(workers
`dbCreateInst(geGetEditCellView() ,inst->master ,inst->name ,inst->origin ,inst->orient)`
)
)
threadPoolJoin(workers) ; 等待所有任务完成
)
)
生产环境避坑指南
坐标系统转换陷阱
工艺厂提供的 GDSII 使用微米单位,而 Skill 默认用内部数据库单位(通常为纳米级)。必须严格转换:
; 错误做法(单位混淆)dbCreateRect(?width 0.18) ; 可能是 0.18nm 或 0.18um?; 正确做法
defvar(*dbUPerUU* 1000) ; 根据 PDK 设置单位比例
width = 0.18 * *dbUPerUU* ; 明确单位转换
工艺库版本兼容性
遇到过因 PDK 升级导致脚本失效的案例:
-
防御性编程:检查层名是否存在
unless(layerExists("M1") error("Current PDK missing M1 layer!") ) -
版本嗅探
pdkVersion = cdfGet(cdFindCellView("tsmc18" "device" "symbol") "pdkVersion") when(pdkVersion != "2.1" warn("Script developed for PDK v2.1, current is %s" pdkVersion) )
进阶挑战:MOS 管阵列生成器
现在尝试实现一个可配置的 MOS 管阵列生成器:
- 输入参数:行数(N)、列数(M)、器件类型
- 自动计算最优 finger 宽度以满足电流要求
- 生成符合 DRC 规则的差分对布局
提示代码框架:
defun(genMosArray(n m mosType)
let((totalWidth fingers)
; TODO: 计算总宽度
; TODO: 自动 finger 划分
; TODO: 生成对称布局
)
)
调试技巧 :在 CIW 窗口使用step 命令单步执行,配合 print 查看变量值
学习路径推荐
- 基础:Cadence 官方《Skill Language User Guide》
- 进阶:Eliot 讲的《Practical Skill Programming》视频课
- 实战:修改 PDK 中的标准单元生成脚本
最后提醒:所有示例代码需在 Cadence IC6.1.7+ 环境验证,建议先用测试库练习操作。遇到问题欢迎在 Cadence Support 社区提问(提问时请附上 skill getVersion() 输出)
正文完
