
简介这份 Cadence SKILL Language User Guide 中文翻译版面向英语阅读较吃力、但具备一定 C 语言基础的技术人员服务于集成电路设计自动化中的 SKILL 脚本学习、查阅与入门培训需求。内容依据产品版本 6.1.6 整理涵盖语言定位、与 Lisp 的关系、基础语法、数据类型标识符、示例代码约定、安装与环境配置、许可说明及技术支持资源等并保留原指南章节结构与检索线索便于按主题快速定位。压缩包内共 1 个 PDF 文件约 68.23MB可作为案头手册、机器翻译参考和模板化查阅素材。已有 527 人学习读者可借助中文译文降低阅读门槛对照示例理解脚本编写、环境搭建与文档查阅路径逐步建立 SKILL 编程基础并为后续定制设计工具和验证流程提供参考。1. 打开一份 Lisp 方言的中文底牌这份翻译文档到底帮谁省时间如果你在模拟版图环境里写过hiSetPoint或dbCreateRect多半绕不开 SKILL。可原厂手册动辄上千页全是英文术语密度高查一个回调函数的参数顺序就得来回翻五六页。这份《Cadence SKILL Language User Guide》中文翻译版对应的原文是 6.1.6 版本、2014 年 11 月发布的产品文档整份内容覆盖语言基础、函数定义、控制流、列表操作和文件读写属于能把 SKILL 从「靠抄同事代码」拉到「自己能写补丁」的那类材料。它适合两类人一是刚进版图或模拟设计组、有点 C 语言底子但被 Lisp 括号劝退的工程师二是想系统性把常用脚本整理成团队模板、却不想每次硬啃英文 PDF 的老手。需要明确一点这是翻译件不是官方中文手册它的价值在「降低第一遍入门的阅读摩擦」而不是替代原厂文档的权威性。先把定位说清楚后面用起来才不会被译文里的个别拗口句子带偏。2. SKILL 是 Lisp 的工业变种先把语法骨架和数据类型立住2.1 SKILL 与 Lisp 的关系以及为什么不能拿「C 思维」直接写脚本文档开篇一章专门讲 SKILL 与 Lisp 的关系这不是凑篇幅。SKILL 的求值模型、符号symbol表示、前缀表达式、列表结构都继承了 Lisp 家族但 Cadence 在工程接口上做了大量扩展。如果你带着 C 的思路写第一周大概会踩两个坑一是习惯写a b c再期待函数返回结果 SKILL 里赋值表达式本身也有返回值容易把中间结果当最终结果用二是把操作符当语法结构看而在 SKILL 里加减乘除、比较、逻辑运算本质上都是函数调用。原文里的例子是(plus 1 2)与(1 2)的关系说明翻译文档也保留了「操作符是 SKILL 函数」这一节。落地理解就是你在括号里写、-、底层走的还是函数分派参数顺序、求值顺序都遵循函数规则。这意味着两件事第一不要指望像 C 那样靠运算符优先级来「猜」结果括号才是硬边界第二你可以把当普通符号传给别的函数这在写通用累加器的时候是能用的。提示机器翻译出来的文档在「操作符」和「函数」混用的段落容易让人迷惑。遇到一个表达式的求值顺序说不清时回到括号的最外层往里一层层剥基本不会错。常见的写法对照如下建议一开始就用免费的小段代码形成肌肉记忆; 前缀写法SKILL 的默认求值形式 (plus 1 2) ; 返回 3 ; 操作符位置看起来像中缀本质仍是函数调用 1 2 ; 返回 3 ; 嵌套时保持前缀可读性更好 (plus (times 2 3) 4) ; 返回 10这里没有花哨技巧核心是让读代码的人不依赖「中缀直觉」。在版图脚本里坐标计算经常嵌套三层以上前缀写法虽然一开始别扭但省去了记优先级的负担。2.2 数据类型六件套数字、字符串、符号、字符、原子、列表翻译文档第 2 章铺开了数据类型但译文里「原子」和「符号」容易看混。按工程用法区分数字和字符串是你会大量直接写进代码的符号symbol通常用作变量名、函数名、属性名很多 API 的入参就是符号而不是字符串比如层名、对象属性键字符是单引号包起来的单个字符用得少但解析字符串时会碰原子atom在文档里是作为「非列表」的统称理解成 Lisp 里的最小数据单元即可列表是 SKILL 的骨架几乎所有参数表和返回值列表都靠它。有一处译文我建议对照英文原文核一下翻译版把t和nil的说明放在「术语和定义」附近表述上不如后文控制流章节直白。实际写条件判断时你就记nil为假除nil以外的一切为真t是习惯用的真值。别在if的条件里写(equal x t)这种比较直接拿x当条件非nil就过这是 Lisp 系脚本最常见的误用之一。; 符号和字符串不要混API 多认符号 layerName M1 ; 字符串 layerSym M1 ; 符号前面有单引号 ; 数字运算直接嵌套 width 0.12 halfW width / 2.0 ; 返回 0.06 ; 列表是最常用的复合结构 point list(0.0 1.5) ; 坐标点参数说明list接收任意数量参数返回列表上面point的结构在调用dbCreateRect之类的版图接口时很常见区别只是有的 API 认两个独立坐标参数有的认一个列表查手册时重点看形参类型别凭习惯改。nil和空列表在多数判断里等价但涉及car取值的接口空列表直接传给car会返回nil后续再对nil取属性就会报错这是新手脚本第一次跑就崩的典型原因。2.3 用最小闭环验证环境加载、执行、看返回值只看不跑是学不会的。这份文档的「开始」一章给了调用函数和看返回值的基本流程落到工程环境里最常见的是在命令行解释器里做三段验证定义变量、调用一个内置函数、观察回显。原文提到的「函数的返回值」一节值得反复看因为 SKILL 里很多函数都静默返回nil表示副作用执行成功比如设置属性如果你习惯 Java 那种「没有返回值就是失败」的思路会把正常返回当异常。; 交互式验证三段式 x 10 ; 赋值回显 10 (y plus(x 5)) ; 回显 15括号包住便于观察 printf(x is %d\n x) ; 打印调试返回 t 或 nil 视实现逻辑说明第二行用额外括号把整个表达式包住是交互调试习惯能让解释器把求值结果作为整体回显翻译文档里没有强调这一点但在排查嵌套表达式时很省事。参数上唯一需要注意的是printf的格式串沿用类 C 风格%d对应整数%s对应字符串传错类型不会崩但输出会乱别在关键日志里依赖它做类型校验。3. 列表与控制流把手册里查得最勤的两块吃透3.1 列表的构造、访问与就地修改以及一个容易翻车的返回值约定翻译文档中间几章关于「SKILL 清单」的部分覆盖了构建列表、访问列表和修改列表。这一块是整份材料里最值得反复翻的因为 SKILL 的工程数据几乎都以列表形式流转版图对象列表、属性键值对、参数表、坐标序列。构建用list、cons访问用car、cdr及其组合修改分两类——返回新列表的函数和就地修改的函数混用这两类是最常见的逻辑错误来源。我的经验是先记「取头」和「取余」两个动作再记组合链条。car取第一个元素cdr取除第一个之外的剩余列表嵌套列表用cadr、cddr这类简写时不要跳着用先用展开写法确认层级再换简写。原文里提到「超右括号」extra right parenthesis这类排版层面的问题翻译版保留了说明实操中它对应的报错往往是「括号不匹配但位置飘了几行」排查时不要盯着报错行看往上回溯到上一个完整的顶层表达式。; 构造与访问 p list(1 2 3 4) first car(p) ; 1 rest cdr(p) ; (2 3 4) second cadr(p) ; 2等价于 car(cdr(p)) ; 就地修改先确认函数是否改变原列表 q cons(0 p) ; p 不变q 为 (0 1 2 3 4) r append(p list(5)) ; 返回新列表p 不变参数和返回值的说明cons的第一个参数是元素、第二个是列表返回新列表append接收两个列表并拼接返回新列表。这两个函数都不会改动原列表是函数式风格的写法。真正要留意的是就地修改类函数翻译文档在「修改列表」一节提到它们会改变原对象并返回该对象一旦你把同一个列表同时传给两条逻辑前一条的修改会静默影响后一条这种 bug 在数据量不大时极难复现。我一般会在函数的入口处对传入列表做一次copy牺牲一点性能换取确定性这在跑批处理脚本时尤其值得。3.2 条件分支if、when、unless、case 各自守什么边界控制流章节把if、when、unless、case、for、foreach放在一起讲翻译版没遗漏但有些例句顺序被打乱了。按使用频次和语义边界重新梳理if是二选一有条件为真和条件为假的执行体when只在条件为真时执行没有其它分支unless是when的反面条件为假才执行case适合等值多分支但它的分派键必须是字面量或常量不要拿运行时计算的字符串硬套否则匹配不上还找不到原因。一个在原文里没有大声强调、但实际会遇到的边界if的第二个分支不写并不等于出错它会返回nil但这在你期望返回值参与后续计算时就是个隐式子。我在脚本里会强制自己给if的两条路径都写明确的返回避免把「副作用函数返回nil」当成「表达式计算失败」来判。; 分支覆盖的写法 if(x 0 then printf(positive\n) else printf(non-positive\n) ) when(x 0 printf(positive path\n) ) case(mode (fast printf(fast mode\n)) (safe printf(safe mode\n)) (t printf(unknown mode, fallback\n)) )逻辑说明case的最后一个分支用t兜底这是 Lisp 系脚本的惯例不给默认分支会返回nil后续取返回值做运算就崩。参数上注意case的分派键是求值后的等值比较你写fast字符串没问题但如果模式值本身是符号两边类型要一致。翻译文档在这一段的例子混用了字符串和符号照抄时先确认你手里的值是哪种。3.3 for 与 foreach循环变量作用域和列表遍历的坑循环语法在译文里占了不小篇幅for和foreach的名字接近用途不同。for是计数循环适合已知次数或递增索引foreach遍历列表变量每次取列表中的一个元素。最容易出错的地方是循环变量的作用域泄漏——在循环结束后继续引用循环变量某些语境下值还留着被当成有效结果使用。; for计数 for(i 0 3 printf(i %d\n i) ) ; foreach遍历列表 shapes list(rect polygon path) foreach(shape shapes printf(shape %s\n shape) )参数说明for的写法是for(变量 初值 终值 循环体)这里终值是否包含取决于具体版本的实现翻译文档在这个边界上表述不算利索稳妥做法是在调试输出里验证一次别凭记忆假设。foreach的列表参数不要传nil空列表下循环体一次都不进逻辑上不报错但如果你在循环外依赖循环变量取结果拿到的就是未定义或旧值。我在批量处理版图对象时会先把对象列表做一次空判断再进foreach省得后面取属性时对nil操作。4. 从调用别人到自己定义函数、参数与加载机制的完整链路4.1 声明一个函数之前先选定正确的定义形式文档「在 SKILL 中创建函数」一章系统讲了procedure、lambda、nprocedure、defmacro、defglofun等定义形式。对绝大多数工程脚本而言procedure是默认选择它声明一个具名函数、可以带参数列表、有返回值。lambda适合匿名函数场景但 SKILL 里函数式接口没有像现代语言那么多实际写版图脚本时lambda出现频率不高。defmacro是宏展开发生在求值前调试成本和阅读成本都高除非在做 DSL 或性能敏感的内联否则普通补丁脚本不要碰。翻译版在「语法函数总结」一节用表格汇总了各定义形式这张表建议打印出来贴在显示器旁边。它的实用价值在于当你从同事手里接过来一个用defun还是nprocedure写的旧脚本先看定义形式大致能判断作者想干什么——nprocedure对参数个数和类型的宽容度与procedure不同老代码兼容性依赖很可能就藏在那里。; 具名函数的标准写法 procedure( MyRectWidth(w h) let((area) area w * h area ) ) ; 调用 MyRectWidth(2.0 3.0) ; 返回 6.0逻辑说明procedure后的第一项是函数名第二项是形参列表之后是函数体。函数体的最后一个表达式值自动作为返回值不需要显式return这是 SKILL 与 C 的另一个认知差异点。参数上形参名称尽量带前缀或团队统一缩写原文在一章里专门讲了「为你的函数选择前缀」目的是避免和内置函数或第三方工具包重名不加前缀的函数名在一个共享环境里被覆盖表现出来是「昨天还能跑的脚本今天静默返回错结果」排查成本极高。4.2 rest、optional、key三类可变参数标记参数定义一节的核心价值在执行rest、optional、key三个选项上。rest收集剩余参数为列表适合写不定参数的包装函数optional声明可省略参数省略时取nilkey支持关键字参数调用时按关键字名传值顺序可以打乱。翻译文档在这里给的例子质量尚可但译文把「关键字参数」和「可选参数」在行文里靠得太近第一次读容易混。; optional后两个参数可省略 procedure( DrawRect(w h optional (l 0.0) (b 0.0)) printf(rect %f %f %f %f\n l b w h) ) DrawRect(2.0 3.0) ; l、b 取默认值 DrawRect(2.0 3.0 1.0) ; b 取默认值 ; key开关式调用 procedure( ExportFile(key name format verbose) printf(export %s as %s\n name format) ) ExportFile(?name cell ?format gds ?verbose t)参数说明optional后的默认值用括号(l 0.0)形式给出只写变量名不写默认值则省略时为nilkey的调用语法是?关键字名 值关键字名前面带问号传参顺序随意但拼写必须和定义一致拼错不会报错只会让对应形参取nil这是个隐蔽的翻车点。我的做法是自定义函数如果参数超过四个一律用key可读性收益远大于多敲几个字符的成本。4.3 let 与 prog局部变量的声明差异和全局状态的管控局部变量章节点名讲了let和prog。简单说let是块作用域局部变量绑定函数返回时绑定撤销prog在旧式 Lisp 里用于带跳转的块结构配套return使用。翻译文档在「初始化局部变量为非空值」一节的表述值得留意译文容易让人以为let只能绑定nil其实带初值的绑定写法是标准用法不初始化并不推荐——未初始化的局部变量在后续分支里被引用很容易拿到上一次调用残留的值。; let 带初值绑定 procedure( Accumulate(lst) let((sum 0) foreach(item lst sum sum item ) sum ) )逻辑说明let的绑定列表是(变量 初值)成对出现示例中sum初始化为0避免foreach空列表情况下返回未定义值。关于全局变量原文在「全局变量」一节提醒了命名和覆盖风险真实项目里更隐蔽的问题是在一段脚本里定义了全局变量被另一段脚本重名覆盖前后跑批时结果不一致。我一般会在团队约定里要求全局变量集中在一个文件里声明脚本入口显式加载别在各处临时定义。4.4 加载与重新定义源码维护的工程化细节「分组 SKILL 陈述」「维护 SKILL 源代码」「加载你的 SKILL 源代码」「重新定义 SKILL 函数」这几小节是这份文档实用度最高的部分之一因为它解决的是「写完放哪、怎么加载、改完怎么生效」的工程问题。load加载源码文件loadContext之类的上下文加载视环境而定。重新定义函数那一节提醒了一个关键事实同名函数后加载的覆盖先加载的不报错。这意味着调试时你以为在跑新版本实际加载路径里旧的同名定义先被找到表现出来就是「代码明明改了结果没变」。; 加载脚本注意路径和返回状态 status load(/path/to/myScript.il) when(status nil printf(load failed, check syntax\n) ) ; 重新定义测试在交互环境里改完函数体后重新 load load(/path/to/myScript.il)参数说明load的传入路径用字符串注意不同运行环境下工作目录不一致能用绝对路径就不要用相对路径。返回值为nil时通常表示加载失败或文件不存在这个返回值要判不要假定加载一定成功。翻译文档在「加载」一节的示例偏简单工程上更常见的做法是在脚本入口加一个版本打印保证你看到运行日志时能确认加载的是哪一版。5. 翻译版避坑清单从血泪踩坑到可复现的排查路径5.1 现象代码块里的函数名被翻成了中文原因机器翻译把代码区域也当成正文处理出现了「函数」「列表」之类词直接替换原符号名的情况。 解决凡是代码块一律以英文原文或你自己环境里验证过的写法为准译文中的代码块只作为句式参考。加载前用肉眼扫一遍符号是否含非 ASCII 字符脚本报「未定义函数」时优先怀疑符号被污染。5.2 现象同一个术语在不同章节译法不一致比如列表和清单混用原因译文由机器翻译产出术语没有统一替换表文档自身的前后一致性弱于原厂手册。 解决建立自己的术语对照表只认结构不认译名。看到「清单」按上下文判断是列表还是清单文件涉及函数名时翻回英文手册核对形参表。不要背译文术语背函数名。5.3 现象按译文的参数顺序写调用运行后结果不对但不报错原因译文在个别函数说明处把形参顺序的叙述打乱了常见于参数较多的构造函数。 解决调用前先做一次类型自检用printf打出每个入参的实际值参数超过三个的函数切换到key写法或先按英文原文核对。凡是静默返回错误值的接口优先怀疑参数位置错位。5.4 现象函数改了但运行结果没变load 也不报错原因同名函数被先加载的旧定义覆盖或加载路径指向了另一份文件。 解决在脚本入口打印加载文件的绝对路径和版本标记用交互环境查函数定义是否生效确认执行的是最新代码。把「改完必须显式重新加载并验证」写进团队流程里别依赖记忆。5.5 现象括号报错定位飘忽指向空行或无关行原因Lisp 系语法的括号错误往往在解析到后续表达式时才暴露译文里关于「超右括号」的说明偏术语化。 解决用编辑器的括号匹配功能从报错位置往上一层层回退到最近的顶层表达式重点查let、procedure、when这类多行结构的收尾括号。写完一段函数就先加载验证再继续写把矛盾集中在一个小范围内解决。6. 把翻译文档用成查表工具进阶技巧与验证方法进入实际使用阶段我建议把这份文档当「第一遍入门材料 日常速查」而不是逐页精读的书。具体做法是两张表加一个习惯。第一张表是「函数定义形式对照表」把procedure、lambda、nprocedure、defmacro的定义语法和典型场景各占一行从翻译文档的总结表里抄出来再补上你自己项目里用到的写法写错一次补一次两三个月就能覆盖八成常用场景。第二张表是「报错-章节」映射把实际遇到的加载失败、括号不匹配、参数错位、同名覆盖逐条对应到文档章节。这个映射的价值在于缩短二次排查时间——同一个坑第二次踩时你不需要重新读整章直接翻到对应小节。场景优先翻的章节验证动作函数报未定义加载与重新定义打印实际加载路径核对符号拼写参数传了但结果为空定义参数逐个打出入参值核对optional、key写法循环结果异常for 与 foreach先单独验证空列表和单元素列表嵌套判断走错分支控制流程每个分支入口加一行日志确认求值路径一个具体技巧把文档里关于返回值语义的小节单独摘出来做成注释模板贴在你写的每个公共函数头部注明「返回什么、失败时返回什么」。听起来像多余动作但我见过太多脚本崩在「副作用函数返回nil被当成功」上尤其批量处理版图对象时前一百个对象处理成功、第一百零一个静默跳过日志里根本看不出来。加一行返回值判断比事后翻原始文档省太多时间。从那以后我每次把新脚本交给别人之前都会强制自己走一遍「加载路径打印、关键函数入参打印、返回值和容错判断」三件事跑通再交。这份翻译文档的最大意义是让你在第一次写 SKILL 时少翻几次英文手册但它的译文边界也必须心里有数代码块符号、参数顺序、术语一致性这三处动手前先核对别直接照抄。希望帮到你。本文还有配套的精品资源点击获取