news 2026/10/15 6:14:42

Cadence SKILL 脚本入门:从 sklanguser 手册到版图自动化实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cadence SKILL 脚本入门:从 sklanguser 手册到版图自动化实战

简介:这份资源是《Cadence SKILL Language User Guide》的中文翻译版,基于产品版本6.1.6,面向英语基础有限但具备一定C语言基础的技术人员,帮助其跨越语言门槛学习Cadence设计自动化流程中的SKILL脚本语言。内容涵盖SKILL语言简介、与Lisp的关系、安装与环境配置、编程指南、语法示例、数据类型标识符以及官方文档、社区论坛、技术书籍等延伸学习资源,并附有前言、许可条款、节名含义等章节说明,便于按主题检索。资源包为1个PDF文件,大小约68.23MB,适合作为系统查阅与入门参考。目前已有523人学习下载,读者可借此掌握SKILL基本语法与核心概念,理解其在电路验证、布局编辑等定制化工具开发中的应用,并借助文档中的学习建议与参考资料快速上手,提升解决实际设计问题的能力。

1. 从一份英文手册到能跑通的脚本:sklanguser 与 Cadence SKILL 的入门路径

很多做模拟版图、定制电路或者 PDK 维护的工程师,第一次接触 Cadence SKILL Language User Guide(圈内常简称 sklanguser)时,都会卡在同一个地方:手册是英文的,语法看着像 Lisp,示例代码又散落在几十个章节里,照着敲进 CIW 里却报一堆看不懂的错。这个标题讲的其实就是一件事——把这份官方语言手册吃透,并且用中文语境把它翻译成自己能复现的操作路径。它解决的不是“SKILL 是什么”这种科普问题,而是“我拿到一份 sklanguser,怎么从零把环境跑起来、把语法用对、把脚本挂到实际流程里”。适合谁?适合刚接手版图自动化、需要写脚本批量改参数、或者要给团队做二次开发的从业者。哪怕你之前只会在 CIW 里敲两句hiSetCurrentWindow,只要愿意按步骤走,这篇笔记能让你少翻几次车。

2. 把 sklanguser 的语法骨架拆开:从 S 表达式到变量作用域

2.1 为什么 SKILL 看起来像 Lisp,但又不是 Lisp

SKILL 的语法根基是 S 表达式,函数调用写成(函数名 参数1 参数2),这一点和 Common Lisp 很像。但 sklanguser 里明确区分了它的求值规则:SKILL 是动态作用域和词法作用域混合的,默认情况下函数体内引用的自由变量会沿着调用栈往上找,而不是像 Scheme 那样只在定义环境里找。这个差异直接决定了你写递归或者回调时会不会踩坑。

我一般会先让新手记住三个最基础的形式:

; 变量定义,注意 SKILL 里用 setq 而不是 set x = 10 setq(y 20) ; 函数定义,prog 是过程体,return 是显式返回 procedure(Add(a b) prog((result) result = a + b return(result) ) ) ; 条件分支,cond 比 if 更常用 cond( (x > y "x is bigger") (x < y "y is bigger") (t "equal") )

逻辑说明:setq是 SKILL 里最常用的赋值形式,procedure定义函数时,参数列表和过程体是分开的,prog用来声明局部变量并组织语句序列。参数说明:prog的第一个参数是局部变量列表,这些变量只在prog内部有效,出了prog就恢复外层同名变量的值——这一点和 C 语言的块作用域类似,但 SKILL 的全局变量默认是动态绑定的,所以命名要格外小心。

2.2 变量作用域与全局状态:为什么你的脚本第二次运行结果不一样

sklanguser 里有一章专门讲prog和let的区别。prog声明的局部变量在退出时会恢复原值,而let不会恢复,它只是临时绑定。很多人在 CIW 里反复执行同一段脚本,发现第一次正常、第二次数据翻倍,八成是因为用了全局变量累加却没重置。

; 错误示范:全局变量累加 procedure(BadCounter() count = count + 1 return(count) ) ; 正确做法:用 prog 声明局部变量,或者显式初始化 procedure(GoodCounter() prog((count) count = 1 return(count) ) )

参数说明:prog的局部变量列表里如果写了count,那么函数内部对count的修改不会影响外层的同名变量。如果你确实需要跨函数共享状态,常见做法是定义一个全局的defvar或者用属性列表(property list)挂载到某个对象上,而不是裸用全局变量。

2.3 用 load 和 eval 把脚本挂进 CIW 的最小命令

手册里讲load的章节往往被跳过,但这是把写好的.il文件跑起来的关键。最小操作步骤:

; 假设你把脚本保存为 /home/user/skill/test.il load("/home/user/skill/test.il") ; 如果只想加载并执行某个函数 loadi("/home/user/skill/test.il") ; loadi 会忽略返回值,适合初始化 ; 查看当前加载路径 getSkillPath()

逻辑说明:load会逐行读取文件并求值,遇到错误会中断并打印行号。loadi是“静默加载”,不返回最后一个表达式的值,适合在.cdsinit里做初始化。参数说明:路径必须是字符串,支持绝对路径和相对路径,相对路径的基准是 CIW 的当前工作目录,不是脚本所在目录——这一点经常让人找不到文件。

提示:在.cdsinit里加载脚本时,建议用errset包一层,避免某个脚本报错导致整个 Cadence 启动失败。

3. 从手册到可复用脚本:用 SKILL 操作版图对象的完整流程

3.1 打开单元格视图并遍历实例的标准写法

sklanguser 里关于数据库访问的部分是重点,但手册的示例往往只给片段。我一般会按这个顺序写:先dbOpenCellViewByType拿到 cellview 对象,再用dbGetInstances或者dbGetOverlaps遍历,最后对每个实例做修改。

procedure(UpdateInstanceParams(libName cellName viewName) let((cv instList inst) cv = dbOpenCellViewByType(libName cellName viewName "layout" "r") unless(cv error("Cannot open cellview: %s %s %s" libName cellName viewName) ) instList = dbGetInstances(cv) foreach(inst instList ; 打印实例名和 master 名 printf("Instance: %s, Master: %s\n" inst->name inst->master->name ) ; 修改某个参数,比如宽长比 ; inst->width = 2.0 ) dbClose(cv) ) )

逻辑说明:dbOpenCellViewByType的第四个参数是视图类型,第五个参数"r"表示只读,如果要修改必须用"a"追加模式或者"w"写模式。dbGetInstances返回的是实例对象列表,每个实例用->访问属性。参数说明:inst->master->name是链式访问,先拿到 master 对象再取名字;如果 master 是 pcell,inst->master可能为 nil,需要先判断。

3.2 用 dbCreateRect 和 dbCreatePath 批量生成图形

手册里对dbCreateRect的参数解释比较分散,我整理成表格:

参数位置含义常用取值
第1个目标 cellviewcv
第2个图层list("M1" "drawing")
第3个矩形坐标list(x1 y1 x2 y2)
第4个可选参数通常省略
procedure(DrawTestRects(cv) let((layer rect) layer = list("M1" "drawing") ; 画一个 0.1um 到 1.0um 的矩形 rect = dbCreateRect(cv layer list(0.1 0.1 1.0 1.0)) ; 画第二个,偏移 2um dbCreateRect(cv layer list(2.1 0.1 3.0 1.0)) printf("Created %d rectangles\n" length(cv->shapes)) ) )

逻辑说明:dbCreateRect返回创建好的矩形对象,可以直接修改它的属性。cv->shapes是 cellview 里所有图形的列表,但注意这个列表是动态的,每次创建后长度会变。参数说明:坐标单位是用户单位(通常是微米),图层必须是在 techfile 里定义过的,否则会报Layer not found。

3.3 把脚本挂到菜单或快捷键:hiSetBindKey 与 hiAddMenuItem

写好的脚本如果每次都要手动 load,效率太低。常见做法是在.cdsinit里注册菜单和快捷键:

; 注册快捷键 Ctrl+Shift+R 执行 UpdateInstanceParams hiSetBindKey("Layout" "Ctrl<Key>Shift<Key>R" "UpdateInstanceParams(\"myLib\" \"myCell\" \"layout\")") ; 添加菜单项 hiAddMenuItem( hiGetCurrentWindow()->menuBar "MyTools" "Update Params" "UpdateInstanceParams(\"myLib\" \"myCell\" \"layout\")" )

逻辑说明:hiSetBindKey的第一个参数是窗口类型,常见的有"Layout"、"Schematic"、"CIW"。第二个参数是按键组合,写法比较固定。第三个参数是字符串形式的 SKILL 表达式,会在按键时求值。参数说明:hiAddMenuItem的菜单栏对象可以通过hiGetCurrentWindow()->menuBar获取,但要注意窗口类型匹配,否则菜单不会出现。

注意:在.cdsinit里注册菜单时,如果 Cadence 还没完全初始化,hiGetCurrentWindow()可能返回 nil,建议用hiRegTimer延迟执行,或者放到hiSetCallback里。

4. 避坑与排查:SKILL 脚本最常见的 5 个翻车现场

4.1 现象:load 时报 “undefined function”,但函数明明定义了

原因:SKILL 加载文件时是顺序求值的,如果函数 A 调用了函数 B,但 B 定义在 A 后面,加载到 A 时 B 还不存在。另外,如果文件里有语法错误导致提前中断,后面的定义也不会生效。

解决:把被调用的函数放在前面,或者用declare提前声明。更稳妥的做法是每个函数单独一个文件,用load按依赖顺序加载。

4.2 现象:修改了实例参数,但版图没更新

原因:dbOpenCellViewByType用了只读模式"r",或者修改后没有调用dbSave。SKILL 对数据库的修改是立即生效的,但如果不保存,关闭 cellview 后修改就丢了。

解决:打开时用"a"模式,修改完调用dbSave(cv),最后dbClose(cv)。如果只是想临时改,可以用dbReopen切换模式。

4.3 现象:foreach 循环里删除列表元素导致漏遍历

原因:foreach在遍历时如果修改了列表本身(比如dbDeleteObject删除了当前对象),列表的内部指针会错位,导致跳过下一个元素。

解决:先收集要删除的对象到临时列表,遍历完再统一删除。或者用for循环从后往前遍历。

; 安全删除写法 let((toDelete) toDelete = nil foreach(inst cv->instances when(inst->name == "dummy" toDelete = cons(inst toDelete) ) ) foreach(inst toDelete dbDeleteObject(inst) ) )

4.4 现象:字符串比较用 == 有时对有时错

原因:SKILL 里==比较的是对象身份,字符串内容比较应该用equal或者strcmp。短字符串可能因为内部驻留机制导致==偶然为真,但长字符串就会翻车。

解决:统一用equal比较字符串内容,用==只比较数字和 nil。

4.5 现象:脚本在 CIW 里跑正常,挂到菜单就报错

原因:菜单回调执行时的上下文和 CIW 不同,hiGetCurrentWindow()可能返回的是 CIW 而不是 Layout 窗口,导致dbOpenCellViewByType拿不到正确的 cellview。

解决:在回调函数里显式传入窗口对象,或者用hiGetCurrentWindow()后判断窗口类型,再决定操作哪个 cellview。

5. 进阶技巧:用 SKILL 做参数化单元与批量验证

5.1 用 pcDefinePCell 定义参数化单元的最小骨架

sklanguser 里对 pcell 的描述偏理论,实际写起来有一套固定模板。下面是一个电阻 pcell 的简化骨架:

pcDefinePCell( list(ddGetObj("myLib") "myRes" "layout") ; 参数列表 ( (width float 1.0) (length float 10.0) (segments int 1) ) ; 过程体 let((cv) cv = pcCellView ; 画主体 dbCreateRect(cv list("RPO" "drawing") list(0.0 0.0 width length)) ; 画两端 dbCreateRect(cv list("M1" "drawing") list(-0.5 0.0 0.0 width)) dbCreateRect(cv list("M1" "drawing") list(length 0.0 length+0.5 width)) ; 返回 cellview cv ) )

逻辑说明:pcDefinePCell的第一个参数是 lib/cell/view 的列表,第二个是参数定义,每个参数用(名字 类型 默认值)描述。过程体里pcCellView是当前正在生成的 cellview 对象。参数说明:float和int是常用类型,还有string、boolean、list。修改参数后 Cadence 会自动重新生成版图。

5.2 批量跑 DRC/LVS 并收集结果的脚本框架

如果团队需要 nightly 跑验证,可以用 SKILL 调strmout和外部工具,但更常见的做法是用 SKILL 生成网表,再交给脚本调度。下面是一个收集 cell 列表并逐个导出的框架:

procedure(RunBatchExport(libName cellList) let((cv outFile) foreach(cellName cellList cv = dbOpenCellViewByType(libName cellName "layout" "r") when(cv outFile = strcat("/tmp/" cellName ".gds") ; 导出 GDS strmout( cv outFile ?libName libName ?cellName cellName ?viewName "layout" ) printf("Exported %s\n" outFile) dbClose(cv) ) ) ) )

逻辑说明:strmout是 SKILL 里导出 GDS 的函数,参数用?key value形式传递。when相当于只有条件为真时才执行。参数说明:?libName和?cellName指定导出的库和单元名,?viewName指定视图。导出后可以用system调用外部 DRC 工具,但要注意路径和权限。

5.3 用 errset 做错误恢复与日志记录

批量脚本最怕中途报错导致整个流程中断。errset可以捕获错误并返回 nil,配合errset.errset拿到错误信息:

procedure(SafeRun(funcName) let((result errMsg) result = errset(eval(funcName)) if(result then printf("Success: %s\n" funcName) car(result) else errMsg = errset.errset printf("Error in %s: %L\n" funcName errMsg) nil ) ) )

逻辑说明:errset返回一个列表,成功时列表里是表达式的值,失败时返回 nil。errset.errset是全局变量,保存了最后一次错误的详细信息。参数说明:eval可以把字符串转成表达式求值,适合动态调用函数名。日志建议同时写到文件和 CIW,方便事后排查。

5.4 一个我常犯的教训

早些年我写过一个批量改参数的脚本,图省事把所有逻辑塞进一个prog里,全局变量满天飞。结果跑第二次的时候数据全乱了,查了半天才发现是某个全局计数器没重置。后来我养成了习惯:每个脚本文件开头先写let或prog把局部变量圈起来,全局变量只用defvar显式声明,并且加注释说明用途。SKILL 的动态作用域很灵活,但灵活的东西最容易出玄学问题,宁可多写几行声明,也别让变量到处乱窜。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 6:14:35

不生成文本的AI日吞一万亿Token:决策模型如何替代生成式大模型

1. 从“不生成文本”说起&#xff1a;一个反直觉的AI决策模型第一次看到“不生成文本的AI日吞一万亿Token”这个说法&#xff0c;我的反应和大多数人一样&#xff1a;不生成文本&#xff0c;那它到底在干什么&#xff1f;毕竟我们早已习惯了大模型“输入提示词、输出一段话”的…

作者头像 李华
网站建设 2026/10/15 6:14:35

SQLite+FTS5实现轻量级AI上下文管理

1. “context-mode”到底是什么&#xff1f;别被术语唬住&#xff0c;它本质是让AI真正“听懂上下文”的工程实践你最近在技术社区、开源项目讨论区&#xff0c;甚至某些企业内部工具文档里&#xff0c;频繁看到“context-mode”这个词——它不像“HTTP”或“Git”那样有明确的…

作者头像 李华
网站建设 2026/10/15 6:14:02

MyBatis-Plus面试高频考点:底层原理、分页插件与实战陷阱

1. 面试官问MyBatis-Plus&#xff0c;到底在问什么先聊点实际的。MyBatis-Plus这个名字&#xff0c;在Java后端面试里出现的频率&#xff0c;这几年是真的高。我身边不少同学去面中小厂和部分大厂&#xff0c;几乎都会在项目环节被问到"你项目里用的ORM是什么""…

作者头像 李华
网站建设 2026/10/15 6:14:00

轻量级服务器自动化巡检工具的设计与落地实践

搞自动化巡检这个事&#xff0c;还得从我接手的那批服务器说起。当时团队里服务器数量涨到了几十台&#xff0c;虽说量不算大&#xff0c;但每天都得人工登录去查 CPU、磁盘、内存这些基础指标&#xff0c;再加上偶尔要追查日志报错&#xff0c;一早上就这么耗进去了。更难受的…

作者头像 李华