Skill教程

准备搜索
Skill教程  
2022/02/06  
Skill 教程  
1
写在前面  
本教程主要面向模拟后端设计工程师.  
学习一门编程语言,最大的意义不在于语言本身能做什么,而是通过一门语言学习和运用,  
改变思维的方式,把一件事情或是一个问题抽象化,用一种标准客观的方式描述它,不断  
地思考如何更有效率的做事  
本教程假定读者对Skill完全不了解, 站在初学者的角度讲解;由于无法实时交流,所以文中  
通过大量标注进行说明. 另外通过丰富的实例,帮助读者进行理解.  
我本人并不是从事编程语言方面专业工作,所以有些描述不够准确严谨,如有谬误,欢迎  
指正.  
2022/02/06  
Skill 教程  
2
周边基础  
1. Linux 基础  
2. 文本编辑器 gvim  
3. 正则表达式  
4. 初始化  
如果这部分内容读者已经熟悉,可以直接跳到讲解Skill的部分  
2022/02/06  
Skill 教程  
4
 
周边基础 01  
Linux  
Linux系统中,用户通过terminal(终端)  
登录系统后得到一个Shell进程,这个  
Shell负责解释和执行命令  
← 在桌面右键打开terminal  
本教程使用CShell + ic618 演示  
管道符 | ,连接两个命令,将前一个命令  
的输出,作为后一个命令的输入  
← 显示变量值  
在当前terminal里设置或是修改的变量,  
只对当前terminal生效,关闭或是重新打  
开一个terminal之后,所做修改都失效  
↑ 管道符  
如果要永久修改,需要修改~/.cshrc文件  
然后source ~/cshrc 即可即刻生效,或者  
重开terminal之后生效  
~ 表示用户的home ()目录,等效于  
/home/$USER  
2022/02/06  
Skill 教程  
5
 
命令  
pwd  
alias  
ls  
功能  
查看当前路径  
设置快捷命令  
查看目录  
命令  
source  
which  
ifconfig  
find  
功能  
加载设置  
周边基础 02  
Linux  
查看命令位置  
查看IP  
mkdir  
tree  
创建目录  
查找  
常用命令以及功能如右表所示  
树状结构查看  
拷贝文件  
top  
查看进程  
几乎所有的Linux命令都有 –h--help 参  
数,可以通过 xxx h xxx help 查看命  
令用法  
cp  
ps  
查看进程  
du  
查看目录大小  
查看所属群组  
更改权限  
kill  
杀进程  
详细的命令用法可以使用 man xxx 查看  
groups  
chmod  
echo  
tar  
cat  
查看文件内容  
查看文件内容  
查看前几行  
查看后几行  
排序  
帮助文档  
more  
head  
tail  
前期不会用到太多,需要使用时查看帮  
助或是百度一下用法示例即可;当你想要  
写自己的Shell脚本时,最好能够熟练运  
用这些命令  
显示  
打包解包  
gtar  
待压缩打包解包  
压缩  
sort  
zip  
grep  
sed  
文本查看处理  
文本查看处理  
文本查看处理  
查看命令帮助  
unzip  
env  
解压  
查看环境变量  
设置环境变量  
awk  
setenv *  
man  
* bash shell 里使用 export  
2022/02/06  
Skill 教程  
6
gvim  
周边基础 03  
文本编辑器 gvim  
Linux下最常用的文本编辑器  
gvimvi里的命令一样,两者的差  
异是gvim会重新打开一个窗口,并  
且支持鼠标操作;vi会占用当前  
terminal,且不支持鼠标.根据你自  
己的习惯选用, 本教程均以gvim作  
为示例  
vi  
启动方式:直接在terminal 输入  
gvim abc  
vi abc  
第一个要学会的gvim命令是退出命  
令,在任意模式下输入  
<esc>:q <Enter>  
2022/02/06  
Skill 教程  
7
 
周边基础 04  
文本编辑器 gvim  
gvim 4种模式  
↑ 普通模式  
普通模式: 新打开一个文件默认是普通模式;  
在任意模式下按<esc>切换为普通模式.普  
通模式下键盘按键都是一个个的命令.  
插入模式: 普通模式下按任意插入命令切换  
为插入模式,插入模式下,键盘按键就是  
输入的内容.  
命令模式: 普通模式下按下冒号 : 切换为命  
令模式 ,命令模式的命令需要<Enter>确  
↑ 命令模式  
↑ 插入模式  
可视模式: 普通模式下按下Ctrl+V切换为块  
可视模式; 按下Shift+V 切换为行可视模式  
左下角有当前模式状态,插入模式的光标  
不同  
本教程只简单介绍前3种模式,待读者对  
gvim熟悉之后,再自行学习可视模式  
↑ 行可视模式  
↑块可视模式  
2022/02/06  
Skill 教程  
8
周边基础 05  
文本编辑器 gvim  
插入命令  
插入命令  
功能  
普通模式下按任意插入命令切换为  
i
I
在当前光标前插入文本  
在行首插入文本  
插入模式  
插入命令如右表所示  
o
O
a
A
在下一行插入文本  
在上一行插入文本  
在当前光标后插入文本  
在行尾插入文本  
插入模式下,键盘按键就是输入的  
内容.  
2022/02/06  
Skill 教程  
9
周边基础 06  
文本编辑器 gvim  
插入命令  
i 命令  
I 命令  
示例:普通模式下,分别输入  
i I o O a A 命令  
注意光标的位置  
<esc>切回到普通模式  
o 命令  
O 命令  
a 命令  
A 命令  
普通模式,光标在w处  
2022/02/06  
Skill 教程  
10  
移动命令  
h j k l  
空格键  
w e  
功能  
左下上右  
周边基础 07  
文本编辑器 gvim  
移动到下一个字符  
移动到下一个单词词首/尾  
移动到上一个单词词首/尾  
移动到行首/尾  
b B  
移动命令  
0 $  
普通模式下,移动命令如右表所示  
^
移动到行首第一个非空字符  
移动到第n行  
插入模式下,移动命令就是方向键  
k
↑ ↓ ← →  
nG  
gvim下还可以使用鼠标进行移动  
gg G  
%
移动到第1/最后1行  
h  
l →  
j
从光标所在的 { } ( ) [ ], 移动到匹  
普通模式下,命令是由1个或多个字  
符组成,只需依次在键盘下输入即  
;如果连续输入多个命令字符,将  
依次执行多个命令  
配的另外一半  
{ }  
移动到当前段落的开头/末尾  
移动到当前屏第一行(head)  
移动到当前屏中间(middle)  
移动到当前屏第一行(last)  
向前滚动1页,向后滚动1页  
向前滚动半页,向后滚动半页  
H
例如输入 ggG ,光标会移动到  
1行,再移动到最后1行  
M
大写字母是指Shift+按键  
L
Ctrl+f Ctrl+b  
Ctrl+u Ctrl+d  
2022/02/06  
Skill 教程  
11  
 
周边基础 08  
文本编辑器 gvim  
编辑命令  
功能  
编辑命令  
d y c  
删除/复制/替换  
删除/复制/替换当前行  
删除一个字符  
普通模式下,命令的组合方式一般形式为  
[执行次数]操作符[定位符]  
操作符是必须的,[ ] 是可选的  
例如  
dd yy cc  
x
X
向前删除一个字符  
替换一个字符  
r
de 删除从光标处到当前单词的词尾,d 表示删除,  
e表示词尾,不包含单词后面的分隔(空格、tab)  
R
u
替换多个字符  
3dw 删除从光标处开始的3个单词,d 表示删除,  
w表示单词,每个单词之间的分隔也算在内会被删  
撤销操作  
Ctrl+r  
U
恢复撤销的操作  
撤销对本行的所有操作  
d$ 从光标处删除到行尾,d表示删除,$表示行尾  
yy 复制当前行  
3yy 复制从当前行开始的3行  
x 删除当前光标所在字符  
3x 删除3个字符  
定位符就是上一节介绍的移动命令  
2022/02/06  
Skill 教程  
12  
周边基础 09  
文本编辑器 gvim  
搜索命令  
普通模式下  
/xxx 向后搜索,输入/, 后面跟要搜索  
的关键字;搜索到之后,n向后搜索下个  
匹配的关键字N向前搜索下个匹配的  
关键字  
↑ 模糊匹配  
?xxx 向前搜索,输入?, 后面跟要搜索  
的关键字;搜索到之后,N向后搜索下个  
匹配的关键字, n向前搜索下个匹配的  
关键字  
输入/ ? 之后,可以按Ctrl+R 快速输  
入当前光标所在处的单词  
/ ? 默认采用模糊匹配方式,如果要  
使用精确匹配,需要前后输入 \< \>  
进行限定,也可以输入 \< \> 进行部  
分精确匹配  
* 搜索当前光标所在的单词, 使用精确  
↑ 精确匹配  
匹配方式,等效于 /\<xxx\>  
2022/02/06  
Skill 教程  
13  
命令模式命令  
功能  
周边基础 10  
文本编辑器 gvim  
:s/aa/bb/  
将当前行第1次出现的aa替换成bb  
:210s/aa/bb/g 从第2行到第10行进行替换操作,将所有  
命令模式  
aa替换成bbg搜索范围内的全局  
普通模式下,输入:切换到命令模式  
:1$s/aa/bb/2 从第1行到最后1行,将每行前两次出现的  
普通模式与命令模式都是输入一些命  
令执行操作,它们之间的区别可以简  
单地理解成普通模式是对文本对象操  
作的局部命令,输入操作符之后即刻  
执行;命令模式是对编辑器操作的全  
局命令,输入操作符之后需要按下  
<Enter>才执行  
aa替换成bb  
:%s/aa/bb/g  
%等价于1$ 从第1行到最后1行,将每行  
所有aa替换成bb  
:w / :q / :wq  
:q!  
保存修改/退出编辑器/保存并退出  
强制退出,! 表示强制执行  
命令模式下可以  
对编辑器进行设置  
查找、替换  
:g/AA/s/aa/bb/g 搜索AA,将含有AA行的所有aa替换成bb  
:g/AA/d  
:set nu / :set nonu  
:set ic / :set noic  
:sp / :vs  
将所有含有AA的行删除  
设置显示行号/关闭行号  
执行外部命令  
命令模式下,命令的组合方式一般形  
设置忽略大小写/关闭忽略大小写  
横向分隔窗口/纵向分隔窗口  
式为  
:[搜素范围]操作符[标识符]  
:和操作符是必须的, [ ] 是可选的  
2022/02/06  
/ 蓝色的/用于分隔不同命令  
Skill 教程  
14  
~/.vimrc 实例  
1. syntax on  
2. set cursorline  
3. set cursorcolumn  
4. set ch=2  
5. set mousehide  
6. set ai  
7. set linebreak  
8. set number  
9. set showmode  
10. set expandtab  
11. set ignorecase  
12. set ruler "show the cursor position all the time  
13. set showcmd  
14. set incsearch  
15. set infercase  
16. set history=20  
周边基础 11  
文本编辑器 gvim  
配置文件  
~/.vimrc gvim的配置文件  
常用配置如右所示  
最后两行配置了Skill API的自动填充  
功能,效果如右图所示,输入单词前  
几个字母按下Ctrl+n可以自动联想  
skill_functions文件格式如下,每行一  
个关键词,这个文件需要自己写  
17. set autoindent  
18. set smartindent  
19. set tabstop=4  
20. set shiftwidth=4  
21. set softtabstop=4  
22. set dictionary=~/.vim/dictionary/skill_functions  
23. set complete-=k complete+=k  
dbCreatePath  
dbCreatePathSeg  
dbCreatePin  
dbCreatePlaceArea  
dbCreatePolygon  
dbCreateProp  
dbCreateRailDef  
↑ 显示行号是gvim功能,便于阅读,代码内容不包括行号,后同  
2022/02/06  
Skill 教程  
15  
周边基础 12  
正则表达式  
正则表达式(regular expression)  
就是描述文本的规则,作为文本  
处理的强大工具,广泛应用于各  
种编程语言和文本处理命令  
语法  
Skill Perl vim  
功能  
.
V
V
V
匹配除换行符以外的任意字符  
匹配 [ ]里面出现的任何字符;如果  
[ ] 里的第一个字符是^,匹配不  
[ ]的任何字符  
[c…]  
V
V
V
常用的正则表达式如右表所示  
[A-Za-z0-9_] V  
V
V
V
V
V
V
V
V
V
V
V
V
V
V
匹配字母、数字或下划线  
等同于[A-Za-z0-9_]  
匹配任意的空白符  
匹配数字  
不同的编程语言对正则表达式的  
支持有些许差异,请查看相应文  
\w  
\s  
\d  
\b  
^
X
X
X
X
V
V
匹配单词的开始或结束  
匹配字符串的开始  
匹配字符串的结束  
$
2022/02/06  
Skill 教程  
16  
周边基础 13  
正则表达式  
语法  
?
Skill Perl vim  
功能  
重复零次或一次  
重复一次或多次  
重复零次或多次  
重复n次  
常用的正则表达式如右表所示  
X
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
V
关于正则表达式,只需要掌握这里列  
出的内容即可覆盖绝大部分的应用,  
更加详细的内容请参考下面这个文档  
+
*
{n}  
{n}  
{nm}  
\(…\)  
\n  
重复n次或更多次  
重复nm次  
分组  
引用分组, n是自然数  
2022/02/06  
Skill 教程  
17  
初始化  
周边基础 14  
3种方式初始化设计环境  
启动Virtuoso  
有以下几种常用方式  
virtuoso &  
.cdsenv  
工具启动时加载.cdsenv, 先生效  
路径 ~/.cdsenv  
语法  
virtuoso nocdsinit &  
layout envVarName datatype value  
不加载任何设置,对于debug非  
常有帮助,可以快速确定是工具  
本身的bug还是用户自己加载的  
设置有问题  
.cdsinit  
工具启动时加载.cdsinit, 后生效  
路径 <work_dir>/.cdsinit > ~/.cdsinit  
语法  
virtuoso replay xxx.log &  
xxx.log当做输入执行一遍  
envSetVal(“layout” “envVarName” ‘datatype value)  
vo &  
环境变量  
alias vo virtuoso (vi is a  
CDS_开头,可以查看你自己的环境设置  
env | grep CDS_  
command of editor)  
语法  
setenv evnVarName value  
2022/02/06  
Skill 教程  
18  
 
~/.cdsenv 实例  
周边基础 15  
初始化  
1. cdsLibManager.main showCategoriesOn boolean t ;effect in current cds only  
lpp显示用到的layer  
2. layout pteShowUsedSystemLpps boolean t  
3. layout drawGridOn boolean nil  
.cdsenv file  
4. layout xSnapSpacing float 0.001  
工具启动时加载.cdsenv, 先生效  
工具默认路径(sample)  
5. layout ySnapSpacing float 0.001  
6. schematic schGridType cyclic "none"  
$CDSHOME/tools/dfII/etc/tools/xx  
x/.cdsenv  
7. ;layout cursorShape boolean t  
8. layout markNetSaveViaInfo boolean t  
9. ui infix boolean t  
10.ui enableMenuShortcuts boolean t  
← 鼠标的类型,十字游标  
用户路径 ~/.cdsenv  
← 执行操作时是否需要点第一下鼠标  
语法  
← 设置快捷键是可以使用Alt组合,  
layout envVarName datatype  
value  
必需放在home  
11.layout leWindowBBox string "((1920 1280) (3840 2560))"  
12.layoutXL layoutWindow string "((1920 1280) (3840 2560))"  
13.layoutXL schematicWindow string "((1920 0) (3840 1280))"  
14.schematic schWindowBBox string "((1920 0) (3840 1280))"  
打开 layout/schematic时默认窗口位置和大小  
19  
2022/02/06  
Skill 教程  
~/.cdsinit 实例  
周边基础 16  
初始化  
1. ddsOpenLibManager()  
←启动时自动打开library manager  
2. skillPath="/home/work/skill/dbk"  
3. if(isDir(skillPath) then  
.cdsinit file  
4.  
5.  
6.  
7.  
8.  
9.  
foreach(file sort(getDirFiles(skillPath) 'alphalessp)  
工具启动时加载.cdsinit, 后生效(有  
if(car(last(parseString(file ".")))=="il" then  
相同设置会覆盖.cdsenv)  
il=strcat(skillPath "/" file)  
sample  
loadi(il)  
$CDSHOME/tools.lnx86/dfII/cds  
printf("Loading %L\n" il)  
)
user/.cdsinit  
用户路径 ~/.cdsinit  
启动时load skillPath下所有的 .il 文件  
10. )  
作用:  
11. printf("Loading %L successfully!\n" skillPath)  
12.)  
设定libraryskill的搜索路径  
设定bind keys  
自定制Skill 脚本  
用户设置  
13.hiSetFilterOptions(t t t t t t t)  
←打开CIW log, 含义见下页  
14.envSetVal("asimenv.startup" "projectDir" `string env("MY_SIM_DIR"))  
↑ 设置默认的仿真路径  
env用于读入环境变量  
读者初学必须要修改的设置  
hiSetFilterOptions(t t t t t t t)  
2022/02/06  
Skill 教程  
20  
周边基础 17  
初始化  
CIW  
.cdsinit文件里的内容都可以直  
接拷贝到CIW里面运行  
.cdsenvcdsinit这两种方式都是  
永久生效;CIW 里面设定只对当  
前打开的设计环境生效  
/o  
/t  
上一页提到的log设置在CIW里设  
置方法以及含义如右图所示  
hiSetFilterOptions(t t t t t t t)  
/o  
/p  
/e  
/t  
2022/02/06  
Skill 教程  
21  
开始!  
经过前面的一番准备工作,我们现在正式进入Skill编程的学习  
你可以同步操作,实践才是学习最快的途径  
2022/02/06  
Skill 教程  
22  
 
Skill简介 01  
SkillCadence Analog Design Environment使用的一门高级交互  
编程语言,基于Lisp语言开发,拥有Lisp语言的很多优秀特性,同  
时支持类C语言语法,初学者也可以很容易学习.  
Skill可以做什么?  
你在GUI里做的一切操作都可以通过Skill实现,例如:  
设置环境:启动窗口的大小、位置、格点,快捷键的设定  
计算、获取数据:图形的坐标、LppPinNet等信息  
自动化操作:创建RectPath,自动打Label,调整PCell 参数  
你在GUI外做的一些操作也可以通过Skill集成,例如:  
调用外部命令:自定义菜单集成第三方工具,流程化操作  
2022/02/06  
Skill 教程  
23  
 
Skill简介 02  
Skill的解释器CIW(Command  
Interpreter Window)以及丰富的  
API(Application Programming  
Interface) 能够让用户迅速、便捷的  
使用Skill  
CIW  
← 返回值  
当你启动virtuoso时显示的主窗口就  
CIW  
← 用户输入  
右边的代码演示了一个接受任意个  
API  
↑ 参数  
数参数的API以及它的输出  
你可以在CIW里输入任意的代码或  
是拷贝过来,随时进行交互式的试  
2022/02/06  
Skill 教程  
24  
Skill学习资源 01  
1. 本教程  
2. cdnshelp  
3. Skill API Finder  
5. 第三方资源,实例  
6. Gitee  
7. $CDSHOME/doc  
初学者按照推荐顺序学习,进阶者直接245交互学习  
2022/02/06  
Skill 教程  
25  
 
Skill学习资源 02  
本教程  
Cadence平台本身提供了非常丰富的学习资源,但是如果你完全没有基础,  
不知从何入手,那么建议先看完本教程,再通过平台提供的资源进行进阶  
与提升;如果你有一些基础,可以快速跳过相关章节,看你感兴趣的部分  
本教程除了介绍Skill语言本身,还会穿插介绍一些其他知识,帮助你更好的  
运用这个工具  
与其它教程不同的是,本教程后面还会提供一些实用的、完整的代码,进  
行讲解,让你能够更快速地进行独立的代码开发,避免像其它教程一样--  
当你学完之后,感觉好像知识点都学到了,但是自己想独立写一个脚本时,  
却无从下手  
2022/02/06  
Skill 教程  
26  
Skill学习资源 03  
← 查找的范围,默认即可  
Skill API Finder  
← 查询的关键字, 可以通过  
组合缩小查询范围  
Skill API Finder 可以显示函数或是API  
的简介与语法,当你想查询某个函数  
的用法,但不知道函数名时,可以根  
据功能猜测.例如,想查询skill的输出  
函数是什么,可以试试查询print,  
Skill API Finder会列出所有含有print的  
函数,然后再依次查看描述,找到所  
需函数,Skill API Finder的优点就是只  
会查找函数名,避免搜索到其它无用  
的信息  
查询的匹配模式,默认即可  
查询结果  
函数相关的类别  
← 语法  
← 返回值  
Skill API Finder 的打开方式  
进入cdnshelp,查询详细介绍  
terminal(终端)下使用命令  
cdsFinder&  
CIW菜单下点击 Tools Skill API  
Finder  
函数的说明  
2022/02/06  
Skill 教程  
27  
Skill学习资源 04  
cdnshelp  
← 查询的关键字  
CDNSHelp DocServercadence的  
在线文档服务,这里提供最全最详  
尽的文档,可以通过以下几个方式  
打开  
terminal下使用命令 cdnshlp&  
随便点开看看 →  
Skill API Finder 界面下点击more  
info  
在任意窗口下点击 Help XXX User  
Guide  
cdnshelp除了可以查看函数说明,  
工具的各种功能也可以在这里找到  
2022/02/06  
Skill 教程  
28  
Skill学习资源 05  
打开library导航,可以查询相关的函数和章节  
← 查询的关键字  
cdnshelp  
CDNSHelp DocServercadence的  
在线文档服务,这里提供最全最详  
尽的文档,可以通过以下几个方式  
打开  
←搜索到的内容  
terminal下使用命令 cdnshlp&  
Skill API Finder 界面下点击more  
info  
在任意窗口下点击 Help XXX User  
Guide  
cdnshelp除了可以查看函数说明,  
工具的各种功能也可以在这里找到  
2022/02/06  
Skill 教程  
29  
Skill学习资源 06  
https://community.cadence.com/ca  
dence_technology_forums/f/custom  
-ic-skill  
Cadence官方论坛,当你看文档遇  
到无法解决的问题可以来这里找一  
.发帖前请先查看置顶说明  
Guidelines for the Custom IC SKILL  
Forum  
问问题前先找一找,如果没有人问过类似  
问题,请详细描述你的问题,最好贴图附  
带说明,越具体越好,不要让别人就你的  
问题还要反复几次才知道你问的是什么  
Andrew大佬很热心,完全是利用个  
人时间在这里解答大家的问题  
这个论坛可以免费注册!  
第三方资源,实例  
eetop  
2022/02/06  
Skill 教程  
30  
函数调用 01  
CIW 里面直接输入  
println(“Hello Kitty”)  
getVersion  
car(list(1 2 3))  
load/loadi skill文件  
读者可以将上述语句写到文件  
里面,通过load文件的方式调  
← 一个参数  
← 无参数,省略了()  
← 函数作为参数被调用  
← 只有一个参数,省略了()  
如果函数没有嵌套,参数没有  
歧义可以省略 ( )  
loadiload相同,除了loadi会  
忽略load过程中遇到的错误,  
打印出错误,然后继续执行后  
面的语句;load遇到错误会终止  
之后的语句  
2022/02/06  
Skill 教程  
31  
 
函数调用 02  
前一页介绍了两种学习过  
程中最常用的方式  
在使用中还会使用menu、  
formbindkeycallback  
等方式调用,在后面的章  
节都会讲解  
注释  
;(英文分号,单行注释)  
/* */ (块注释)  
2022/02/06  
Skill 教程  
32  
数据类型 01  
常用数据类型如右表所示  
查看数据类型 type()  
数据类型  
Integer  
类型符号  
示例  
判断类型函数  
integerp()  
floatp()  
type(1) fixnum  
type(1.0) flonum  
type(“hello”) string  
type(()) list  
x
f
10  
10.0  
floating point  
symbol  
s
s
l
‘foo  
symbolp()  
stringp()  
type(‘foo) symbol  
type(t) symbol  
type(nil) list  
string  
“hello kitty”  
list(1 2 3) ‘(1 (2 3) 4)  
t nil  
list  
listp()  
判断数据类型  
boolean  
b
booleanp()  
各种数据类型都有对应函数判断是  
否是此类型,通常以数据类型+字  
p表示  
是返回t;否返回nil  
floatp(1.0) t  
floatp(1) nil  
2022/02/06  
Skill 教程  
33  
 
list(显示)  
(1 2 3)  
说明  
含有3个整数的list  
数据类型 list 01  
(1 abc2.0)  
(1 (2 3) 4)  
()  
含有3个不同数据类型的list  
嵌套的list,长度为3  
listSkill数据对象的有序集  
. listSkill语言的核心,用  
途非常广泛. list的元素可以  
是任意的数据类型,包括变  
量,也可以任意嵌套  
list,等价于 nil,长度为0  
含有一个空元素的list,长度为1  
坐标x:y,也是一个list,等价于 (1.0 2.5)  
含有变量的list  
(nil)  
1.0:2.5  
a=“abc”  
有两种方式创建list  
使用list函数  
list(1 2 3)  
(1 a)  
x 还未赋值, 作为一个symbol元素  
x 还未赋值,不能引用  
使用单引号 操作符  
(1 2 3)  
← 变量 x 已赋值  
二者的区别是list函数先计算表达式再  
创建list; 操作符直接使用字面组成list  
a b 不相等  
2022/02/06  
Skill 教程  
34  
 
x=‘(1 2 3 4 5 6 7)  
y=‘(((1 2 3) (4 5 6)) (7 8 9))  
数据类型 list 02  
获取list中的元素  
函数  
返回值  
说明  
元素  
car(x)  
1
car获取list的第一个元素  
cdr(x)  
last(x)  
(2 3 4 5 67)  
list  
cdr获取list除去第一个元素的剩下部  
(7)  
3
list  
nth(2 x)  
car(y)  
元素  
last获取list的最后一个元素组成的list  
((1 2 3) (4 5 6))  
(1 2 3)  
1
元素(是个嵌套list)  
等价于car(car(y))  
等价于car(car(car(y)))  
等价于cdr(car(y))  
等价于cdr(cdr(car(y)))  
nth获取list的第n个元素,n0开始  
caar(y)  
caaar(y)  
cdar(y)  
cddar(y)  
……  
ca|d[a|d][a|d][a|d]r carcdr的不同排  
列组合  
((4 5 6))  
(1 2 3)  
助记: c1234r()=c1r(c2r(c3r(c4r())  
2022/02/06  
Skill 教程  
35  
x=‘(1 2 3) y=‘(4 5 6)  
数据类型 list 03  
list添加元素  
cons/xcons list头部添加元素  
append1list尾部添加元素  
函数  
返回值  
(4 1 2 3)  
(4 1 2 3)  
说明  
cons(4 x)  
xcons(x 4)  
x 不变  
cons作用相同,参数位置不同  
car(last(k)) 获取最后一个元素 4  
k=append1(x 4)  
z=tconc(nil 1)  
tconc(z 2)  
(1 2 3 4)  
((1) 1)  
tconc list尾部添加元素,速度更  
((1 2) 2)  
修改了z本身  
修改了z本身  
合并list  
tconc(z 3)  
((1 2 3) 3)  
((1 2 3 4) 4)  
(1 2 3 4 5 6)  
(1 2 3 4 5 6)  
append  
tconc(z 4)  
cadr(z) 获取z最后一个元素 4  
x 不变  
nconc 速度更快  
lconc 速度最快  
append(x y)  
nconc(x y)  
x 被修改为 (1 2 3 4 5 6)  
注意:tconc/nconc/lconc都是  
破坏性函数,都修改了原list,  
所以不需要再赋值给原变量  
lconc(z ‘(5 6)) ((1 2 3 4 5 6) 6) 利用tconc结构,修改了z本身  
2022/02/06  
Skill 教程  
36  
x=‘(2 4 6 8 10) y=x  
数据类型 list 04  
list删除元素  
remove 返回一个新list  
remd 破坏性函数  
函数  
返回值  
说明  
z=remove(6 x) (2 4 8 10)  
x(2 4 6 8 10)  
y(2 4 6 8 10)  
remd(6 x)  
remd(2 x)  
(2 4 8 10)  
(2 4 8 10)  
x(2 4 8 10)  
y(2 4 8 10)  
注意:list的赋值实际上是  
指针的引用,例如 a=‘(1  
2) b=a, 变量a b 共享  
(1 2),当a被修改时,  
b也同时被修改  
list的第1个元素不会被修改,  
x(2 4 8 10)  
y(2 4 8 10)  
x=remd(2 x)  
(4 8 10)  
x被重新赋值  
x (4 8 10)  
y(2 4 8 10)  
2022/02/06  
Skill 教程  
37  
数据类型 list 05  
替换list元素  
x=‘(2 4 6) y=‘(2 4 6)  
rplaca 替换第一个元素  
rplacd 替换除第一个元素之外剩  
函数  
返回值  
说明  
下的部分  
rplaca(x 8)  
(8 4 6)  
x(8 4 6)  
y ( 2 4 6)  
注意:这两个函数都是破坏性函数  
rplacd(y ‘(1 3 5)) (2 1 3 5)  
x(8 4 6)  
y(2 1 3 5)  
remd(2 x)  
x=remd(2 x)  
2022/02/06  
Skill 教程  
38  
x=‘(2 4 1 3 4) y=‘(“bag” “Zoom” “dog”)  
数据类型 list 06  
其它函数  
函数  
返回值  
说明  
member(4 x)  
(4 1 3 4) 返回从查看元素第一次出现到结  
member 判断元素是否存在于list  
length 获取list长度  
reverse 倒序list  
setof 过滤  
尾的部分,不为空说明存在  
member(5 x)  
length(x)  
nil  
返回值nil,说明不存在  
长度为5  
5
length(nil)  
length(‘(nil))  
reverse(x)  
0
1
nil等效于长度为0 list  
长度为1  
sort 排序  
(4 3 1 4 2)  
倒序  
setof(y x evenp(y)) (2 4 4)  
过滤出偶数  
setof(y x y>2)  
(4 3 4)  
过滤出大于2的数  
数字从小到大排序  
字符串按照字母排序  
sort(x ‘lessp)  
(1 2 3 4 4)  
sort(y ‘alphalessp) (“Zoom”  
“bag”  
“dog”)  
2022/02/06  
Skill 教程  
39  
数据类型 list 07  
提示  
前面只介绍了list最常用的  
函数,其余未讲到的函数可  
以查阅文档  
2022/02/06  
Skill 教程  
40  
Y
pUR=xUR:yUR  
数据类型 list 08  
常用list  
bBox  
point:  
X:Y  
bBox(bounding box):  
pLL=xLL:yLL  
list(xLL:yLL xUR:yUR)  
X
有多个函数可以获取X Y  
函数  
返回值  
函数  
返回值  
transform  
caar(bBox)  
leftEdge(bBox)  
xLL  
car(pLL)  
xCoord(pLL)  
xLL  
list(x:y “rotation” 1.0)  
坐标、旋转、倍数  
cadar(bBox)  
yLL  
cadr(pLL)  
yLL  
bottomEdge(bBox)  
yCoord(pLL)  
caadr(bBox)  
leftEdge(bBox)  
xUR  
yUR  
centerBox(bBox) ((xLL+xUR)/2  
(yLL+yUR)/2)  
cadadr(bBox)  
topEdge(bBox)  
2022/02/06  
Skill 教程  
41  
可打印字符  
A-Za-z0-9_  
! ? - + . * …  
数据类型 string 01  
string(字符串)是字符的有序  
集合,用标识,string由以  
下两类字符组成  
不可打印字符  
new-line  
转义字符  
可打印字符(除了)就是字符本身  
\n  
\t  
不可打印字符和本身, 需要在前  
horizontal tab  
vertical tab  
面加上转义字符\  
\v  
backspace  
\b  
\r  
carriage return  
form feed  
\f  
backslash  
\\  
double quote  
ASCII code ddd(octal)  
\”  
\ddd  
2022/02/06  
Skill 教程  
42  
 
数据类型 string 02  
string 相关函数  
函数  
返回值  
说明  
strcat(“AB” “cd” “3”)  
length(“ABcd3”)  
length(“”)  
“ABcd3”  
连接字符串  
如右表所示  
5
获取字符串长度  
空字符长度为0  
转义字符长度为1  
空白符如下:  
space (空格)  
\r (回车)  
0
1
length(“\n”)  
buildString(  
“AB cd 3”  
list构造字符串,默  
\n (换行)  
‘(“AB” “cd” “3”))  
认分隔符是空白符  
\t (制表符 Tab)  
\v (纵向制表符)  
\f (换页符)  
buildString(  
‘(“AB” “cd” “3”) “-)  
“AB-cd-3” 由list构造字符串,指  
定分隔符为 -  
parseString(“A-B a-b”) (“A-B” “a-b”) string转换成list,默  
认分隔符为空白符  
parseString(  
(“A” “B a” “b”) string转换成list,指  
“A-B parse String” “-)  
定分隔符为 -  
2022/02/06  
Skill 教程  
43  
函数  
返回值  
说明  
index(“abcbdbce” ‘b)  
“bcbdbce” 截取从第2个参数第1次  
数据类型 string 03  
开始的剩余字符串  
string 相关函数  
index(“abcbdbce” “bc”)  
“bcbdbce” 截取从第2个参数第1次  
如右表所示  
开始的剩余字符串  
字符串比较是指从左到右依次比较  
每个字符的大小,按第1个不相等  
的字符决定两个字符串大小  
rindex(“abcbdbce”  
“bc”)  
“bce”  
2
从右截取第2个参数第1  
次开始的剩余字符串  
nindex(“abcbdbce”  
“bc”)  
获取第2个参数开始的  
字符的大小按照字符在ASCII表中的  
位置决定,排在前面的小,排在后  
面的大  
索引  
nindex(“abcbdbce”  
“bg”)  
nil  
nil说明stirng不存在第2  
个参数  
strcmp(“abc” “abb”)  
strcmp(“abc” “abc”)  
strcmp(“abc” “abd”)  
strncmp(“abc” “ab”3 )  
strncmp(“abc” “de” 4)  
strncmp(“abc” “ab” 2)  
Skill 教程  
1
0
按字符比较字符串  
按字符比较字符串  
-1  
1
按字符比较字符串  
指定最多比较几个字符  
指定最多比较几个字符  
指定最多比较几个字符  
-1  
0
2022/02/06  
44  
数据类型 string 04  
string 相关函数  
函数  
返回值  
说明  
如右表所示  
sort(‘(“ab” “34” “XY” “123”)  
(“123” “34”  
“XY” “ab”)  
字符串排序  
alphalessp)  
substring((“abcdefgh” 2 4)  
substring((“abcdefgh” 4 2)  
substring((“abcdefgh” -4 2)  
“bcde”  
从第2个字符开始截  
4个字符  
“de”  
从第4个字符开始截  
2个字符  
“ef”  
从右侧第4个字符开  
始截取2个字符  
upperCase(“abCdE”)  
“ABCDE”  
“abcde”  
string转换成大写  
lowerCase(“abCdE”)  
string转换成小写  
2022/02/06  
Skill 教程  
45  
数据类型 string 05  
string 相关函数  
函数  
返回值  
说明  
rexMatchp("[0-9]*[.][0-9][0-9]*" "100.001")  
rexMatchp("[0-9]*[.][0-9]+" ".001")  
rexMatchp("[0-9]*[.][0-9]+" ".")  
rexMatchp("[0-9]*[.][0-9][0-9]*" "10.")  
rexCompile("^[0-9].*")  
t
查找pattern,有  
查找pattern,有  
查找pattern,无  
查找pattern,无  
rex编译,用于查找  
rex编译,用于查找  
正则 rexXXX()  
t
注意 rexExecute 根据最后一次  
rexCompilepattern进行查找,如  
果它们之间使用了rexMatchp, 那  
rexCompilepattern也会被修改,  
所以要确保rexExecute查找的  
nil  
nil  
patternrexCompile 一致  
rexCompile("^[a-zA-z].*")  
t
t
rexExecute(abc123)  
根据“^[a-zA-z].*”  
查找,有  
rexExecute("123abc")  
nil  
根据“^[a-zA-z].*”  
查找,无  
rexMatchp(“AB" “abc123")  
nil  
nil  
pattern被改成 “AB“  
rexExecute(abc123)  
查找 “AB“,无  
2022/02/06  
Skill 教程  
46  
数据类型 string 06  
string 相关函数  
正则替换,语法  
rexReplace(  
t_source  
函数  
rexCompile( "[0-9]+" )  
返回值  
t
rexReplace( "abc-123-xyz-890-wuv" "(*)" 1) "abc-(*)-xyz-890-wuv"  
rexReplace( "abc-123-xyz-890-wuv" "(*)" 2) "abc-123-xyz-(*)-wuv"  
rexReplace( "abc-123-xyz-890-wuv" "(*)" 3) "abc-123-xyz-890-wuv"  
rexReplace( "abc-123-xyz-890-wuv" "(*)" 0) "abc-(*)-xyz-(*)-wuv"  
t_replacement  
x_index  
)
rexReplace是在字符串t_source里查  
找到的pattern替换成指定的字符串  
t_replacement,第3个参数 x_index  
表示替换几次,0 表示全部替换  
rexCompile("^teststr")  
t
rexReplace("teststr_a" "bb" 0)  
rexReplace("teststr_a" "bb&" 0)  
rexReplace("teststr_a" "[&]" 0)  
"bb_a"  
"bbteststr_a"  
"[teststr]_a"  
2个参数t_replacement里的 & 字  
符表示查找到的pattern  
rexSubstituterexReplace的替换相  
反,是将pattern保留下来,具体用  
法可以查阅文档  
2022/02/06  
Skill 教程  
47  
数据类型 string 07  
string 相关函数  
正则 pcreXXX()  
函数  
返回值  
说明  
pcreMatchp("[0-9]*[.][0-9][0-9]*" "100.001")  
pcreMatchp("[0-9]*[.][0-9]+" ".001")  
pcreMatchp("[0-9]*[.][0-9]+" ".")  
pcreMatchp("[0-9]*[.][0-9][0-9]*" "10.")  
p1=pcreCompile("^[0-9].*")  
t
t
查找pattern,有  
查找pattern,有  
查找pattern,无  
查找pattern,无  
pattern 赋值给p1  
pattern 赋值给p2  
根据p2查找,有  
根据p1查找,有  
PCRE(pcre) (Perl Compatible  
Regular Expressions) Perl兼容  
的正则匹配,更加灵活强大  
nil  
nil  
pcre  
pcre  
t
pcreMatchp rexMatchp 使用方  
式一样  
pcreCompile 编译的pattern 可以  
赋值给变量,然后进行调用,不  
rexCompile 必须使用最后一次  
的编译pattern  
p2=pcreCompile("^[a-zA-z].*")  
pcreExecute(p2 abc123)  
pcreExecute(p1 "123abc")  
t
建议使用pcreXXX函数替代  
rexXXX函数  
2022/02/06  
Skill 教程  
48  
数据类型 string 08  
函数  
返回值  
string 相关函数  
正则替换,语法  
pcreReplace(  
o_comPatObj  
t_source  
p1=pcreCompile( "[0-9]+" )  
t
pcreReplace( p1 "abc-123-xyz-890-wuv"  
"(*)" 1)  
"abc-(*)-xyz-890-wuv"  
pcreReplace(p1 "abc-123-xyz-890-wuv"  
"(*)" 2)  
"abc-123-xyz-(*)-wuv"  
"abc-123-xyz-890-wuv"  
"abc-(*)-xyz-(*)-wuv"  
t_replacement  
x_index  
pcreReplace(p1 "abc-123-xyz-890-wuv"  
"(*)" 3)  
[x_options]  
pcreReplace(p1 "abc-123-xyz-890-wuv"  
"(*)" 0)  
)
p2=pcreCompile("^teststr")  
t
pcreReplacerexReplace类似,只  
pcreReplace(p2 "teststr_a" "bb" 0)  
pcreReplace(p2 "teststr_a" "bb&" 0)  
pcreReplace(p2 "teststr_a" "[&]" 0)  
"bb_a"  
是需要指定pattern  
"bbteststr_a"  
"[teststr]_a"  
pcreSubstitutepcreReplace的替  
换相反,是将pattern保留下来,具  
体用法可以查阅文档  
2022/02/06  
Skill 教程  
49  
函数  
abs(-1)  
返回值  
说明  
绝对值  
数据类型 number  
number 相关函数  
1
3
1
max(1 2 3)  
min(1 2 3)  
evenp oddp  
floatp intp  
float(1)  
最大值  
最小值  
如右表所示  
判断偶数/奇数  
判断浮点数/整数  
转换成浮点数  
转换成整数  
取余  
数字分为浮点数和小数  
整数之间加减乘除结果还是整数  
1.0  
1
整数与浮点数、浮点数与浮点数之  
间运算结果是浮点数  
int(1.2)  
其余未讲到的函数可以查阅文档  
mod(9 2)  
modf(9.2 2.0)  
ceiling(3.5)  
floor(3.5)  
round(3.5)  
round(3.49)  
sqrt(4)  
1
1.2  
4
浮点数取余  
向上取整  
3
向下取整  
4
四舍五入取整  
四舍五入取整  
开平方  
3
2.0  
2022/02/06  
Skill 教程  
50  
 
变量  
Skill里的变量不需要事先声明  
← 全局变量  
变量由字母/数字/下划线/问号组成  
← 局部变量  
A-Za-z0-9_?  
← 局部变量let() 域内有效  
不建议使用 ?  
不建议使用数字开头  
命名有意义  
作用域  
← 局部变量在域外使用报错  
全局变量  
默认就是全局变量,在脚本中  
尽量避免使用全局变量,容易  
引起不易察觉的错误  
局部变量  
使用let 或是prog 定义局部变  
量,局部变量只在作用域内生  
2022/02/06  
Skill 教程  
51  
 
operator 01  
operator(操作符)是进行数学  
或逻辑运算的符号,每个操  
作符都有对应的函数例如:  
← 注意,不要加多余的()  
2*(3+1)  
times(2 plus(3 1))  
()改变优先级,在进行数学或是逻  
辑运算时,多,使用()可以使运算过  
程更加清晰  
完整的运算符请查阅文档的关键字  
Arithmetic and Logical Operators  
2022/02/06  
Skill 教程  
52  
 
比较运算符  
2>1  
返回值  
t
nil  
t
operator 02  
比较运算符  
2<1  
2>=1  
2<=1  
2!=1  
逻辑运算符  
list的逻辑值为nil  
其它逻辑值为t  
nil  
t
2==1  
nil  
逻辑运算符  
返回  
说明  
nil  
2
t && nil  
0 && 1 && 2  
t || nil  
所有值都为t时,则返回t,否则返回nil  
如果都为t,则返回最后的值  
所有值都为nil时,则返回nil,否则返回t  
如果有值为t,返回第一个非nil值  
取反  
1
nil || 1 || 2  
!t  
t
nil  
t
!list()  
取反  
2022/02/06  
Skill 教程  
53  
函数 01  
Skill里的function(函数)可以  
1. strcat("Hello" " Kitty")  
2. ;--> "Hello Kitty"  
3. (strcat "Hello" " Kitty")  
4. ;--> "Hello Kitty"  
5. strcat "Hello" " Kitty"  
6. ;--> "Hello Kitty"  
7. load "~/test.il”  
8. ;--> t  
C format  
使用以下几种格式:  
lisp format  
C format:  
← 省略 ( )  
Lisp format:  
如果不是子表达式, 可以省略最  
外面的()  
以上3种格式可以混用  
9. strcat("Hello"  
10. " Kitty")  
建议采用C format, 符合大多数  
语言习惯  
;--> 表示返回值  
11. ;--> "Hello Kitty"  
函数内括号内可换行  
一行可以写多个函数,空格分开,  
以上代码可以拷贝到CIW执行,前面的行号勿拷贝  
不建议  
2022/02/06  
Skill 教程  
54  
 
↓ 函数名  
1. procedure(myFunc1()  
2.  
printf("hello kitty\n")  
3. )  
函数 02  
使用procedure定义自己的函  
数,有如下几种形式  
4. myFunc1() ← 无参数  
5. ;--> hello kitty  
6. procedure(myFunc2(name color)  
无参数  
7.  
printf("This is %s\n" name)  
procedure(FunName()  
8.  
printf("%s is %s\n" name color)  
…body…  
9. )  
)
10. myFunc2("apple" "red")  
← 固定位置参数  
11.  
12.  
;--> This is apple  
;--> apple is red  
固定位置参数  
procedure(funName(arg1 [arg2…])  
…body…  
13. procedure(myFun3(@rest args)  
)
14.  
foreach(x args  
args 传进来是一个list  
15.  
printf("%s\n" x)  
)
任意个数参数  
16.  
procedure(funName(@rest args)  
17. )  
…body…  
18. myFun3("blue" "red" "green")  
← 任意个数参数  
)
19.  
20.  
21.  
;--> blue  
;--> red  
;--> green  
2022/02/06  
Skill 教程  
55  
可选参数,必须给定默认值 ↓  
1. procedure(myFunc4(@optional (color "red"))  
函数 03  
可选参数(默认值参数)  
2.  
printf("apple is %s\n" color)  
3. )  
4. myFunc4()  
5. ;--> apple is red  
6. myFunc4("yellow")  
7. ;--> apple is yellow  
← 如无参数就用默认值  
procedure(FunName(@optional (arg1  
val1)  
[(arg2 val2) … ])  
← 如有参数就用给定值  
…body…  
)
关键字参数,给定默认值 ↓  
8. procedure(myFunc5(@key (name "apple") (color "red"))  
关键字参数  
9.  
printf("%s is %s\n" name color)  
procedure(funName(arg1 [arg2…])  
…body…  
10. )  
)
11. myFunc5()  
12.  
;--> apple is red  
13. myFunc5(?name "pear")  
← 关键字参数调用时,即使只有一个参  
数,也必须指定参数名和参数值  
14.  
;--> pear is red  
15. myFunc5(?color "blue" ?name "pear")  
↑ 关键字参数调用时,位置可以任意;  
16.  
;--> pear is blue  
参数名和参数值必须配对  
2022/02/06  
Skill 教程  
56  
↓ 固定位置参数必须在前  
1. procedure(myFunc6(name @optional (color "red"))  
函数 04  
组合参数  
2.  
printf("%s is %s\n" name color)  
3. )  
procedure(funName(arg1 [arg2…]  
@optional (argo1 val1) [(argo2 val2)…]  
4. myFunc6("apple")  
5. ;--> apple is red  
6. myFunc6("apple" "green")  
7. ;--> apple is green  
← 固定位置参数必须给值  
…body…  
)
↓ 固定位置参数必须在前  
8. procedure(myFunc7(name @key (size "big") (color "red"))  
procedure(funName(arg1 [arg2…]  
@key (argo1 val1) [(argo2 val2)…]  
9.  
printf("%s %s is %s\n" size name color)  
…body…  
10. )  
)
11. myFunc7("apple")  
12.  
; --> big apple is red  
@optional @key 不能同时使用  
13. myFunc7("pear" ?color "yellow")  
14.  
;--> big pear is yellow  
15. myFunc7("apple" ?color "green" ?size "small")  
16.  
;--> small apple is green  
2022/02/06  
Skill 教程  
57  
函数 05  
组合参数  
1. procedure(myPlus(a @optional (b 1) @rest args)  
@rest 在最后面  
2.  
sum=a+b  
3.  
foreach(x args sum=sum+x)  
sum  
procedure(funName(arg1 [arg2…]  
@optional (argo1 val1) [(argo2 val2)…]  
4.  
@rest args  
5. )  
…body…  
6. myPlus(1)  
7. ;--> 2  
8. myPlus(1 2)  
9. ;--> 3  
10. myPlus(1 2 3 4)  
11. ;--> 10  
12. plus(1)  
← 固定位置参数必须给值  
)
@rest @optional或是@key混用时,  
@rest必须在最后面  
← 内置函数必须至少提供两个参数  
;--> *Error* plus: too few arguments (at least 2 expected1  
13.  
given)  
2022/02/06  
Skill 教程  
58  
1. procedure(myFunc8(a)  
2.  
let((b)  
b=2  
b 局部变量  
3.  
4.  
if(a>b then  
a-b  
else  
a+b  
函数 06  
5.  
6.  
7.  
作用域,在前面讲变量时  
有提到:  
8. )))  
9. myFunc8(1)  
全局变量  
1+2  
10.  
;--> 3  
11. myFunc8(6)  
默认就是全局变量,在  
脚本中尽量避免使用全  
局变量,容易引起不易  
察觉的错误  
6-2  
12.  
;--> 4  
13. procedure(myFunc9(a)  
14.  
if(a>b then  
a-b  
15.  
局部变量  
16.  
else  
17.  
a+b  
使用let 或是prog 定义局  
部变量,局部变量只在  
作用域内生效  
18. ))  
myFunc8 里的b是局部变量,对这里不生效  
19. myFunc9(1)  
20.  
;--> *Error* eval: unbound variable - b  
全局变量在函数之外也可以  
使用,可以用作函数之间进  
行数据传递,但是应该避免  
这样使用.在函数内部应该使  
let或是prog来定于局部变  
21. b=2  
22. myFunc9(1)  
← 在函数外设定变量b  
1+2  
23.  
;--> 3  
24. b=7  
← 在函数外修改变量b  
25. myFunc9(6)  
6+7  
26.  
27. myFunc8(6)  
28. ;--> 4  
;--> 13  
← 在函数外修改变量,对myFunc8 里的b无效  
2022/02/06  
Skill 教程  
59  
1. procedure(myFunc10()  
函数 07  
返回值,每个函数都有一  
个返回值  
2.  
let(()  
a=1  
b=2  
3.  
4.  
5. ))  
let里,函数的最后一个表  
达式或是语句的结果作为返  
回值  
← 函数返回值可以直接赋值给一个变量  
6. x=myFunc10()  
7.  
;--> 2  
prog里使用return明确指定  
返回值, 如果没有return,则  
返回 nil  
8. x  
9.  
;--> 2  
10. procedure(myFunc11()  
prog里,可以有多个return  
语句,遇到哪个return,就执  
行哪个return;执行return语句  
之后跳出当前函数,return之  
后的语句不再执行  
11.  
prog(()  
a=1  
12.  
13.  
return(a)  
return返回一个值后,跳出程序,后面不再执行  
14.  
b=2  
15. ))  
16. myFunc11()  
17.  
;--> 1  
2022/02/06  
Skill 教程  
60  
1. myPower=lambda((x y) x**y)  
2. apply(myPower '(3 2))  
← 无函数名,赋值给myPower  
函数 08  
匿名函数 lambda  
← 调用  
3.  
;--> 9  
procedure定义函数,需要指定  
函数名不同,lambda定义一个匿  
名函数,它返回一个函数对象,  
可以赋值给一个变量  
4. ss = '(  
5.  
6.  
7.  
8.  
("c" 1.5 )  
("a" 0.4 )  
("b" 2.5 )  
)
lambda 函数经常导致代码难以理  
解,建议使用procedure定义函数;  
但是在某些情况下或者需要传递  
一个很小功能的函数时,lambda  
函数就非常有用了  
9. sort(ss  
ss 按照第2个元素从小到大排序  
10.  
11.  
12.  
13.  
14.  
15.  
lambda((a b) cadr(a)<=cadr(b))  
)
;--> (("a" 0.4)  
("c" 1.5)  
("b" 2.5)  
)
2022/02/06  
Skill 教程  
61  
↓ 创建一个名为myStructstructure,含有3个属性,分别是s1 s2 s3  
1. defstruct(myStruct s1 s2 s3)  
2. ;--> t  
3. gv= make_myStruct(?s1 "red" ?s2 "blue")  
数据结构与~>  
← 给其中2个属性赋值  
在介绍其它类型API时,先介绍一个最  
常用的数据结构 defstruct 以及相关操  
作符 ~> . defstruct 是一个包含一个或  
多个 属性名-属性值 对的集合  
4.  
;--> myStruct@0x22971c38  
固定语法,make_<s_name>s_name 就是defstruct  
里定义的名字,myStruct将这个structure赋值给变量 gv  
5. gv~>s1  
6.  
;--> "red"  
defstruct(  
← 通过 ~> 操作符获取属性值  
7. gv~>s2  
s_name  
s_slot1  
[slot2 …]  
8.  
;--> "blue"  
9. gv~>s3  
10.  
;--> nil  
← 未赋值的属性值为nil  
)
11. gv~>?  
← 通过 ~>? 操作符获取所有属性名  
gv=make_s_name(  
?s_slot1 value1  
[?s_slot1 value1 …]  
)
12.  
;--> (s3 s2 s1)  
← 通过 ~>?? 操作符获取所有属性名与属性值  
13. gv~>??  
14.  
;--> (s3 nil s2 "blue" s1 "red")  
← 给 s3 动态赋值  
Skilldefstructpython语言的字典类  
似,属性名-属性值对应python的键命  
-键值, 都有不同的方法(~>? ~>??)  
获取属性名或者属性名-属性值  
15. gv~>s3="green"  
16.  
;--> "green"  
17. gv~>lpp = list("M1" "drawing")  
← 添加一个属性 lpp 并赋值  
18.  
19. gv~>??  
20. ;--> (s3 nil s2 "blue" s1 "red" lpp ("M1" "drawing"))  
;--> ("M1" "drawing")  
其它的数据结构请查阅文档, 关键字  
data structure  
2022/02/06  
Skill 教程  
62  
 
1. print(123)  
2. ;--> 123  
3. print(123) print(456)  
4. ;--> 123456  
5. println(123) println(456)  
← 无换行  
← 无换行  
输出 01  
Skill 最常用的输出函数如下  
print 输出内容  
← 有换行  
6.  
7.  
;--> 123  
456  
println 输出内容,末尾会额外打印  
一个换行符  
8. printf("format:%L\n" 123.123)  
← 使用printf() 记得要\n换行  
printf 格式化输出, 常用格式如下  
%L 默认格式  
9.  
10. printf("format:%.2f\n" 123.123)  
11. ;--> format:123.12  
12. printf("format:1234567890\n" )  
13. ;--> format:1234567890  
14. printf("format:%9.3f\n" 123.123)  
15. ;--> format: 123.123  
;--> format:123.123  
← 格式化输出,%L是默认格式  
%d 整数  
%n.mf 浮点数 (n长度 m精度,  
2位精度  
- 左对齐)  
%-ns string ( n长度,-左对齐)  
n长度是指如果内容的长度小于n,则  
用空格填充;如果内容长度大于n,则按  
照实际长度输出  
9位长度,2位精度,默认右对齐  
16. printf("format:%-9.3f\n" 123.123)  
17.  
;--> format:123.123  
9位长度,2位精度,左对齐,后  
面还有2个空格没显示  
2022/02/06  
Skill 教程  
63  
 
Format  
Type(s) of  
Argument  
Prints  
%d  
%o  
%x  
%f  
整数  
整数  
十进制整数  
八进制整数  
输出 02  
完整的格式如右表所示,  
可以应用于所有支持格式  
化输出的函数  
整数  
十六进制整数  
浮点数  
浮点数  
浮点数 [-]ddd.ddd  
科学计数法 [-]d.ddde[-]ddd  
%e  
sprintf  
fprintf  
warn  
info  
根据值的大小自动选择 %f 或者 %e,有可能  
%g  
浮点数  
有精度损失  
%s  
%c  
%n  
%P  
%B  
stringsymbol 不含引号的stirng或者是symbol的名字  
stringsymbol  
第一个字符  
数字  
整数、浮点数  
error  
list  
list  
X:Y 形式的点坐标  
(X1:Y1 X:Y2)形式的Box  
Prints an object in the old stylethat is,  
%N  
%L  
any  
list  
does not call the printself function  
默认格式  
Prints any type of object using the printself  
representation  
%A  
any  
2022/02/06  
Skill 教程  
64  
1. shapeType="rect"  
2. layer="M1"  
3. if(shapeType=="rect"  
4.  
then  
println( "Shape is a rect")  
else  
println( "Shape is not a rect")  
Flow Control 01  
Flow Control (流程控制)就是通过  
判断不同的条件,执行不同的任务,  
包括branch(分支) Iteration(迭代)  
5.  
6.  
7.  
8. )  
9. if(shapeType=="rect"  
branch 包括:  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18. )  
then  
if(layer=="M1" then  
println( "Shape is a rectlayer is Melal 1")  
if(… then else …)  
← 嵌套 if  
if 语句的条件为真时,执行then  
后面的语句;为假时,执行else后面  
的语句.  
)
else  
if 语句可以嵌套, else 分支可选  
if(layer=="M1" then  
println( "Shape is not a rectlayer is Metal 1")  
条件判断时,除了nillist() 其余都  
)
← 省略了else  
为真  
需要注意, if ( 之间没有空格  
when  
unless  
case  
19. if(shapeType then  
shapeType 的逻辑值为真,执行then部分  
20.  
println("Shape is True")  
21. else  
cond  
22.  
println("Shpae is False")  
23. )  
2022/02/06  
Skill 教程  
65  
 
1. if(t then  
Flow Control 02  
Flow Control (流程控制)就是通过  
判断不同的条件,执行不同的任务,  
包括branch(分支) Iteration(迭代)  
2.  
println("condition is True")  
3. )  
4. when(t  
← 与 if(t then … 作用一样,更简洁清晰  
branch 包括:  
if(… then else …)  
when  
5.  
println("condition is True")  
6. )  
如果只有then部分执行时候,使用  
7. if(! t then  
when语句. 结构更加清晰  
8.  
println("condition is False")  
unless  
9. )  
如果只有条件为假时执行,使用  
unless  
10. unless(nil  
← 与 if(! t then … 作用一样,更简洁清晰  
与if(… then…) 正好相反,避免了假  
假为真这种令人困惑的写法  
11.  
println("condition is False")  
case  
12. )  
cond  
2022/02/06  
Skill 教程  
66  
Flow Control 03  
Flow Control (流程控制)就是通过  
判断不同的条件,执行不同的任务,  
包括branch(分支) Iteration(迭代)  
1. procedure(test(x)  
2.  
case(x  
("red"  
println("red apple")  
3.  
color 如果不是red,跳到下个分支  
color 如果不是yellow,跳到下个分支  
color 如果不是green,跳到下个分支  
← 当上面所有都不满足时,执行这里  
4.  
5.  
)
6.  
("yellow"  
branch 包括:  
if(… then else …)  
when  
7.  
println("yellow pear")  
8.  
)
9.  
("green"  
10.  
11.  
12.  
13.  
14.  
15. )  
16. )  
println("green watermelon")  
)
unless  
(t  
case  
println("orange orang")  
使用case可以进行多个条件的判断  
)
case 语句自上而下依次比较变量的  
值,如果有相等的,执行当前分支;  
后面的分支不再执行  
17. test("red")  
18. test("yellow")  
19. test("green")  
20. test("blue")  
如果所有的值都不匹配,则执行最  
后的t分支  
cond  
2022/02/06  
Skill 教程  
67  
Flow Control 04  
1. procedure(test(x)  
2.  
cond(  
(stringp(x)  
Flow Control (流程控制)就是通过  
判断不同的条件,执行不同的任务,  
包括branch(分支) Iteration(迭代)  
3.  
← 测试x是否是字符串  
4.  
printf(“%s is string\n" x)  
5.  
)
← 测试x是否是浮点数  
6.  
(floatp(x)  
branch 包括:  
if(… then else …)  
when  
7.  
printf("%f is float point\n" x)  
8.  
)
9.  
(fixp(x)  
printf("%d is integer\n" x)  
← 测试x是否是整数  
10.  
11.  
12.  
13.  
14.  
15.  
16. )  
unless  
)
case  
(t  
← 当上面所有都不满足时,执行这里  
printf("%L is another type\n" x)  
cond  
)
case 类似,可以做更复杂条件的  
)
判断  
cond 语句自上而下依次进行测试,  
如果条件满足,执行当前分支;后面  
的分支不再执行  
17. test(1)  
18. test(1.0)  
19. test("abc")  
20. test(t)  
如果所有测试条件都不满足,则执  
行最后的t分支  
2022/02/06  
Skill 教程  
68  
1. i=0  
2. while((i <= 5)  
← 知道结束的条件  
3.  
printf("%d\n" i)  
Flow Control 05  
4.  
i++  
← 注意,必须要保证条件能够终止  
5. )  
Flow Control (流程控制)就是通  
过判断不同的条件,执行不同的  
任务, 包括branch(分支) 和  
Iteration(迭代)  
6. sum=0  
7. for(i 1 5  
← 知道变量起始和结束的范围  
8.  
sum=sum + i  
printf("%d\n" sum)  
9.  
Iteration 包括:  
while  
10. )  
11. foreach(x '(1 2 3 4 5)  
← 遍历一个可迭代对象所有元素  
for  
12.  
printf("%d\n" x)  
13. )  
foreach  
← 遍历两个可迭代对象所有元素  
14. foreach((x y) '(1 2 3) '(4 5 6)  
15.  
printf("%d+%d=%d\n" x y x+y)  
16. )  
17. foreach((x y) '(1 2 3) '(4 5)  
← 两个可迭代对象长度不同时,  
18.  
printf("%d+%d=%d\n" x y x+y)  
按照短的长度遍历  
19. )  
foreach mapcar 用法  
20. foreach(mapcar x '(1 2 3 4) list(x x**2))  
2022/02/06  
Skill 教程  
69  
1. ;打印乘法口诀表  
← 打开文件  
2. outf=outfile("test_file")  
File Writing/Reading  
3. when(outf  
← 文件打开并可写时,才执行后续写入操作  
4.  
for(i 1 9  
for(j 1 i  
fprintf(outf "%dX%d=%-2d " j i i*j)  
写入到文件需要3个步骤  
5.  
1. outfile 打开文件并获取输出端口,需  
6.  
要确保输出与文件具有可写权限  
↑ 格式化写入文件  
7.  
)
2. fprintf 格式化写入数据到端口  
8.  
fprintf(outf "\n")  
3. close 关闭文件(输出端口)  
9.  
)
10.  
11. )  
close(outf)  
← 关闭文件  
读取文件同样需要3个步骤  
1. infile 打开文件并获取输出端口  
12. inf=infile("test_file")  
13. when(inf  
← 打开文件  
2. fscanf 从输入端口格式化读入数据,  
读取的方式是以”word” (即以空白符  
(空格、Tab、换行符)为分隔的字符  
) 为单位依次读取,然后赋值给变  
← 打开文件成功,才执行后续读取操作  
← 三个word三个word 读入  
14.  
while(fscanf(inf "%s %s %s" a b c)  
printf("%s %s %s\n" a b c)  
15.  
← 读取的数据不包括换行符,所  
以这里需要换行  
16.  
)
3. close 关闭文件(输出端口)  
17.  
close(inf)  
← 关闭文件  
18. )  
2022/02/06  
Skill 教程  
70  
 
File Reading  
读取文件还有其它方式,  
同样需要3个步骤  
1. inf=infile( "~/.cshrc" )  
2. when(inf  
1. infile 打开文件并获取输出端口  
← 以行为单位读取数据并赋值给变量  
3.  
while(gets(line inf )  
4.  
println(line)  
2. gets 从输入端口以行为单位读取  
← 换行符也包括在读取的字符串中,记得  
在处理数据时,把结尾的换行符删除.  
另外,打印时需要再加入换行符  
数据  
5.  
)
3. close 关闭文件(输出端口)  
6.  
close(inf)  
如果仅仅是想看一个文件,可以  
7. )  
使用 view  
其它的读取写入API请查阅文档的  
8. view("~/.cshrc")  
← 常用于弹出说明  
关键字  
Input Output Function  
2022/02/06  
Skill 教程  
71  
异常  
CIW里输入代码时,  
经常会遇到语法错误,  
常见原因如下  
常见错误  
解决方法  
() 不匹配  
] 关闭所有括号  
“] 关闭所有 双引号和括号  
换成英文符号  
“” 不匹配  
输入中文符号 () ; “”  
函数与 ( 之间有空格  
仔细检查  
2022/02/06  
Skill 教程  
72  
 
KeyName  
key  
键位说明和动作  
就是键盘上的那些按键例如a 2,按下  
符号类按键需要写英文名字,例如 要写comma  
Control key,控制键,需要与其他键组合使用  
Shift key,控制键,需要与其他键组合使用  
Alt key,控制键,需要与其他键组合使用  
鼠标左键按下  
BindKey 01  
key  
首先,认识一下键盘上的key如何  
表示,请看右表  
Ctrl  
注意不要在CIW上设置那些可打印  
key, 例如 A-Za-z0-9 以及各种  
符号,否则会导致你在CIW里面无  
法进行输入  
Shift  
Alt  
Btn1Down  
Btn2Down  
Btn3Down  
Btn4Down  
Btn5Down  
DrawThru1  
DrawThru2  
DrawThru3  
不要在CIW设置设置鼠标左键;不要  
设置F1 F2 F3  
鼠标中键按下  
鼠标右键按下  
鼠标滚轮前滚  
鼠标滚轮后滚  
鼠标左键按下并且拖动  
鼠标中键按下并且拖动  
鼠标右键按下并且拖动  
2022/02/06  
Skill 教程  
73  
 
BindKey 02  
hiSetBindKey(  
t_appType  
t_key  
1. hiSetBindKey( "Command Interpreter" "<Key>F2"  
CIW窗口F2打开librarymananer  
2.  
3. hiSetBindKey("Command Interpreter" "None<Key>F6"  
CIW窗口F6打开退出窗口  
"ddsOpenLibManager()")  
4.  
"hiQuit( )")  
5. hiSetBindKey("Schematics" "<Key>1"  
schematic view 1键取消所有net高亮  
t_skill_cmd  
)
6.  
"geDeleteAllProbe(getCurrentWindow() t)")  
7. hiSetBindKey("Schematics" "<Key>2"  
schematic view 2键取消所选net高亮  
设置一个bindkey,其中的所有参数类  
型都是一个string  
8.  
"geDeleteNetProbe()")  
t_appType bindkey应用的类型,我  
们最常用的就是 “Layout”  
9. hiSetBindKey("Schematics" "<DrawThru3>"  
10. "hiZoomIn( )")  
schematic view 鼠标右键拖动放大  
layout viewShift+D键删除ruler  
layout viewD键打开ruler  
“Schematics” “Command  
11.hiSetBindKey( "Layout" "Shift<Key>D"  
12. "leHiClearMeasurement()")  
Interpreter” , 如果不指定的话,就  
会弹出bindkey设置窗口, 你可以  
手动设置bindkey  
13.hiSetBindKey( "Layout" "<Key>D"  
14. "leHiCreateMeasurement()")  
t_key 按键组合,包括键盘上各个按  
键、控制键、鼠标  
t_skill_cmd 一个/skill命令  
2022/02/06  
Skill 教程  
74  
1. hiSetBindKey("Layout" "<Key>space" "geSave()")  
2. hiSetBindKey("Layout" "Ctrl Shift<Key>1"  
← 空格键保存  
BindKey 03  
hiSetBindKey 示例  
Ctrl+Shift+1键 打印两句话,在CIW输出  
3.  
4.  
"println(\"Hello Kitty\")  
warn(\"Hello Kitty\")")  
5. hiSetBindKey("Layout" "Ctrl<Btn1Down>" "println(enterPoint()")  
注意:  
Ctrl+左键 打印左键点击的坐标  
每个参数都是一个字符串,参数  
3命令里如果有 “” , 需要用 \ 进  
行转义  
6. hiSetBindKey("Layout" "Shift<Btn1Down>" "println(enterBox())")  
Shift+左键 打印左键划过的矩形bBox  
7. hiSetBindKey("Layout" "<Btn1Down>(2)" "leHiEditProp()")  
如果有多个命令组成,每个命令  
↑ 左键双击 打开属性窗口(Q)  
之间用空格分隔即可  
8. hiSetBindKey("Layout" "<Key>2"  
如果鼠标按键双击,需要写成  
2键在LSW显示 VIA1 VIA2, 并同时  
选中M2  
<BtnxDown>(2)  
9.  
"pteSetVisible(\"VIA1 drawing\" t)  
10. pteSetVisible(\"VIA2 drawing\" t)  
11. pteSetActiveLpp(\"M2 drawing\")")  
12.hiSetBindKey("Layout" "<Key>\\“  
13. "pteSetActiveLpp(\"AP drawing\")")  
14.hiSetBindKey("Layout" "<Key>;“  
同一个按键的上键位,需要用  
Shift+主键 表示,例如冒号 : ,  
要用Shift+ ;表示;大写字母同理  
\ 键在LSW选中AP层  
; 键在LSW选中RV层  
15. "pteSetActiveLpp(\"RV drawing\")")  
2022/02/06  
Skill 教程  
75  
1. hiSetBindKeys( "Layout" list(  
BindKey 04  
hiSetBindKeys(  
t_appType  
t_bindkeyList  
)
2.  
3.  
4.  
5.  
6.  
7.  
8.  
9.  
list("Shift<Key>D" "leHiClearMeasurement()")  
list("<Key>D" "leHiCreateMeasurement()")  
list("<Key>space" "geSave()")  
list("Ctrl Shift<Key>1"  
"println(\"Hello Kitty\")  
warn(\"Hello Kitty\")")  
一次性设置多个bindkey  
list("Ctrl<Btn1Down>" "println(enterPoint()")  
list("Shift<Btn1Down>" "println(enterBox())")  
t_appType bindkey应用的类型,同  
hiSetBindKey  
10. list("<Btn1Down>(2)" "leHiEditProp()")  
11. list("<Key>2"  
t_bindkeyList 是一个包含bindkey组  
合的list,格式如下  
12.  
13.  
14.  
"pteSetVisible(\"VIA1 drawing\" t)  
pteSetVisible(\"VIA2 drawing\" t)  
pteSetActiveLpp(\"M2 drawing\")")  
list(  
list(t_key t_skill_cmd)  
[list(t_key t_skill_cmd) …]  
15. list("<Key>\\" "pteSetActiveLpp(\"AP drawing\")")  
16. list("<Key>;" "pteSetActiveLpp(\"RV drawing\")")  
)
↑ 将前面所有单独设置的layout相关的bindkey一次性设置  
17. ))  
2022/02/06  
Skill 教程  
76  
BindKey 05  
不错,现在你应该学会如何设置自己  
的快捷键了. 这里没有讲GUI方式设置  
bindkey,是因为你学会用命令方式  
设置bindkey之后几乎就不再会用到  
GUI的方式了. 但是你仍可以通过GUI  
的方式查看其它的bindkey如何设置  
你也可以通过这个命令找到工具提供  
的一些默认的bindkey文件  
find $CDSHOME –iname “*bindkeys.il*”  
提示:建议将做常用的命令设置到键  
盘左手位,不常用的命令设置到右手  
位,有利于提升速度  
问题:如果你想设置很多bindkey,键  
↑将当前生效的bindkey保存为一个文件  
位不够用了,怎么办?  
手动设置bindkey  
待学习更多的API之后,我们再解决  
这个问题  
2022/02/06  
Skill 教程  
77  
API的命名规则  
API的命名规则如下  
prefix  
le  
含义或类别  
Layout Edit GUI)  
schematicGUI)  
GUI)  
类别 动作 对象  
类别:通过不同的前缀区分,常用前缀  
与含义如右表所示  
sch  
hi  
动作: 常用的 Get Set, 其它如 Create  
Add Select Open Close Move Align  
Copy Save 等等  
ge  
Graphic EditGUI)  
database None GUI)  
对象:例如 Pin Rect Inst Path Wire  
CellView等等  
db  
所以当你要找API时,先根据上面的规  
则查找关键字,然后查看说明,以确  
定是否所需API  
dd  
pte  
cdf  
Palette, 包括LSW/Objects/Grids等  
CDF(Component Description Format)  
2022/02/06  
Skill 教程  
78  
 
实战  
到这,我们基本介绍完了Skill语言最基本的内容,包括数据类型、变量、  
操作符、函数、流程控制.如果都掌握了,恭喜你;如果还没有,也不用急,  
至少很多概念都有所了解,不会一眼看去觉得很茫然,无从下手  
接下来,我们会从不同类型的API入手,逐步做一些有实用价值的东西,  
后面涉及的API不会讲解参数含义,请自行查阅文档  
示例,命令或代码片段,仅做演示  
实例,有完整功能  
2022/02/06  
Skill 教程  
79  
 
依次打开3layout view: win_a/win_b/win_c  
Window VS View 01  
winID:window(4)  
winID:window(3)  
winID:window(2)  
我们接下来的操作很多都是对layout操作,  
需要了解一些windowview的区别  
window是一个容器,是所有GUI显示的  
一个区域,每个window都有一个ID,  
window(n), 当我们启动Virtuoso时,  
CIW是第一个打开的window,所以CIW  
ID就是 window(1),  
cvID: db:0x1f3bbe1a  
cvID: db:0x1f3bb59a  
cvID: db:0x1f3bae9a  
当我们每打开一个cell view,这个cell  
viewwindow ID都会递增;同时这个cell  
view 本身也有一个cell view ID  
↓ 再打开一个layout view win_cwinID: window(5)  
当我们在GUI 进行操作时,不需要关注  
window IDcell view ID的区别;当我们执  
行一些命令或是写脚本时,需要知道你  
对什么对象进行操作,所以需要知道它  
们之间的区别  
hiGetCurrentWindow() 获取window ID  
geGetEditCellView() 获取cellView ID  
添加menu、弹出window等等这些是对  
window进行操作;操作shapelabel等等  
图形是对cellview里的对象进行操作  
2022/02/06  
Skill 教程  
80  
 
win_a里通过 x或是Shift+x 进入到下一层 win_c  
Window VS View 02  
winID:window(4)  
winID:window(3)  
winID:window(2)  
打开一个window之后,它的window ID  
是固定的,当通过xShift+x 进入到底  
层的view时,cellView ID 会变化  
通过不同的方式打开一个cell view 它的  
cvID: db:0x1f3bbe1a  
cvID: db:0x1f3bb59a  
cvID: db:0x1f3bae9a  
cellview ID是固定的  
你可以试着打开不同的window或是  
cellview,然后在CIW里执行  
hiGetCurrentWindow()  
geGetEditCellView()  
↓ 再打开一个layout view win_cwinID: window(5)  
看一看  
注意,你可以将上述命令执行的结果  
赋值给不同的变量, 供后续调用或查  
当你看到db:0x1f3bbe1a这类字符串时,  
说明它是指向一个内存地址,你不能  
通过直接输入这个字符串来引用,必  
须通过赋值给一个变量,来引用这个  
变量  
2022/02/06  
Skill 教程  
81  
1. cv=geGetEditCellView()  
← 获取当前的cellview,并赋值给cv  
← 创建rectangle  
2. s1=dbCreateRect(cv '("M1" "drawing") '(0:1 3:4))  
3. s2=dbCreatePath(cv '("M2" "drawing")  
← 创建path  
示例 01  
4.  
5. s3=dbCreatePolygon(cv '("M3" "drawing")  
6. '((0 1) (-0.5 1) (-0.5 2) (-1 2) (-1 0) (0 0)))  
7. dbCreateLabel(cv '("text" "drawing") 1:5 "out_1"  
8. "centerLeft" "R0" "stick" 1)  
9. dbCreateLabel(cv '("text" "drawing") 2:4 "out_3"  
10. "centerLeft" "R0" "stick" 1)  
11.dbCreateLabel(cv '("text" "drawing") 3:8 "out_2"  
12. "centerLeft" "R0" "stick" 1)  
13.s4=dbCreateLabel(cv '("M1" "drawing") 3:2 "out_2"  
14. "centerLeft" "R0" "stick" 1)  
'(0:0 0:1 1:1 1:2 2:2) 0.3)  
← 创建polygon  
我们以下面右边示例来创建一些图型,  
← 创建label  
得到的效果如下图所示  
15.s5=dbCopyShape(s2 cv '(0:3 "R0" 1))  
16.s6=dbCopyShape(s3 cv '(0:3 "R0" 1))  
17.leMakeCell( '(s5 s6) "TEST" "testa" "layout" nil )  
← 拷贝图形  
make cell  
18.s7=dbCreateInstByMasterName(cv "TEST" "testa" "layout"  
← 调用刚才创建的instance  
19.  
20.cv2=dbOpenCellViewByType( "TEST" "testa" "layout"  
← 以只读模式打开cell,并赋值给cv2  
"inst1" 3:0 "R0" 1)  
21.  
"maskLayout" "r")  
← 创建Array  
22.dbCreateSimpleMosaic(cv cv2 "my_array" 0:5 "R0" 2 3 3 3 )  
23.fg=dbCreateFigGroup(cv "My_group" t 1:1 "R0")  
← 创建group  
24.dbAddFigToFigGroup(fg s3)  
← 将对象添加到group  
25.dbAddFigToFigGroup(fg s4)  
2022/02/06  
Skill 教程  
82  
 
1. cv=geGetEditCellView()  
2. cv~>?  
← 获取当前的cellview,并赋值给cv  
示例 02  
← 查看cv的属性名  
我们以下面这个实例来看看从中能获  
3. cv~>??  
← 查看cv的属性名和属性值  
取一些什么信息  
← 查看cvbBox大小  
4. cv~>bBox  
任意打开一个layout view,在CIW执  
← 查看cv的所有shape(图形)  
5. cv~>shapes  
6. cv>lpps  
行这些命令, 并同时观察输出结果  
← 查看cv所用层次,每个对象都是一层次  
7. cv~>lpps~>layerName  
8. cv~>lpps~>nShapes  
9. cv~>vias  
← 继续查看这些层次的名称.~>可以多次使用  
← 查看这些层次的shape的个数  
← 查看cv里调用的vianil表示没有  
10. ;layout view里面用鼠标选中path  
11. geGetSelSet()~>objType  
12. ;layout view里面用鼠标选中label  
← 查看选中对象的objType属性  
← 选中的对象赋值给sel,注意geGetSelSet()返  
回的是一个list  
13. sel=geGEtSelSet()  
14. sel~>xy  
15. sel~>lpp  
← 查看选中对象的坐标  
← 查看选中对象的层次  
2022/02/06  
Skill 教程  
83  
1. procedure(putSortedLabelOnYAxis()  
2. let((cv labels test_l y)  
← 如果不需要获取返回值,使用let性能更好  
← 获取当前的cellview,并赋值给cv  
← 获取cv里的所有shape图形  
3.  
4.  
5.  
cv=geGetEditCellView()  
shapes=cv~>shapes  
示例 03  
我们现在找到所有用text 层打的label,  
然后按照从大到小的顺序,step=2 的  
间隔放置到y轴上  
labels=setof(x shapes x~>objType=="label")  
↑ 根据objType过滤出所有的label  
6.  
text_l=setof(x labels x~>layerName=="text")  
效果如下  
↑ 再根据layerName过滤出text层的label  
这里涉及的所有知识点以及API,前面  
都有讲到,只有排序的过程有点不好  
理解,多看几遍,慢慢体会吧  
7.  
sorted_text=sort(text_l  
lambda((a b)  
← 对text_l 按照字母顺序从大到小排序  
8.  
9.  
plusp(alphaNumCmp(a~>theLabel b~>theLabel))  
↑ 排序关键是使用了匿名函数lambda进行判断,lambda的  
10.  
11.  
12.  
);lambda  
第二个参数要返回一个boolean; 但是alphaNumCmp 比较  
两个 string,返回的是-1/0/1, 所以通过plusp测试正负性  
来返回boolean值,以此来判断string 大小.sort 会以此对所  
有的元素进行比较,最终返回一个排序之后的值  
);sort  
y=0  
13.  
foreach(obj sorted_text  
← 遍历sorted_text, 对每个元素进行下面操  
14.  
obj~>xy = 0:y  
← 直接通过修改labelxy来改变位置, 移动一  
个对象就是这么简单,修改它的xy属性即可  
15.  
y = y+2  
)
16.  
17. ))  
2022/02/06  
Skill 教程  
84  
实例 01  
1. procedure(dbkCrossRuler()  
写一个十字标尺,以当前鼠标位置为参  
考点,分别测量到当前view上下左右4边  
的距离,并设置一个bindkey进行调用  
2. let((cv bbox top bottom left right xy)  
3.  
cv=geGetEditCellView()  
bbox=cv~>bBox  
← 获取当前cellview,并赋值给cv  
4.  
← 获取当前cellviewbBox  
效果如下  
5.  
top=topEdge(bbx)  
提示: 通过查阅关键字找到合适的API  
获取鼠标位置,getpoint  
6.  
bottom=bottomEdge(bbox)  
left=leftEdge(bbox)  
分别获取bbox 上下左右4个边的值  
7.  
创建标尺,ruler  
8.  
right=rightEdge(bbox)  
9.  
xy=hiGetPoint(getCurrentWindow()) ← 获取当前鼠标的坐标xy  
10.  
11.  
12.  
13.  
14. ))  
dbCreateRuler(cv list(xy left:cadr(xy)))  
dbCreateRuler(cv list(xy right:cadr(xy)))  
xy为参考点,  
分别创建4ruler  
dbCreateRuler(cv list(xy car(xy):top))  
dbCreateRuler(cv list(xy car(xy):bottom))  
15. hiSetBindKey("Layout" "<Key>v" "dbkCrossRuler()")  
↑ 设置一个bindkey  
2022/02/06  
Skill 教程  
85  
IDE 01  
Skill IDE 提供一些额外工具用于开发和调试skill代码  
CIW: Tools SKILL IDE 打开IDE  
最常用的就是断点和变量跟踪  
IDE: Window Assistants Breakpoints 打开断  
点窗口  
Next/Step/Step Out/Continue/Stop  
通过在代码区域点击行号设置断点,可设置多个断点  
设置断点后会出现一只小手图标  
当代码运行到断点位置时会暂定,等待用户指示  
通过Next/Step/Step Out/Continue/Stop 等按钮来调试  
执行到第12行时暂停;注  
意观察第1011行已经执  
行完成,第12行未执行  
IDE: Window Assistants Trace 打开跟踪窗  
设置待  
跟踪变量  
可以设置需要跟踪的Variable或是Function  
这里只对IDE的调试功能做基本的介绍,我自己平时  
都是使用printf大法来进行调试,读者可以根据自己  
的习惯学习使用  
点击设置断点  
2022/02/06  
Skill 教程  
86  
 
New/Open/Open and Load/Load 一个skill 代码  
IDE 02  
Skill IDE 还提供了lint工具用于检查skill代码  
IDE: Window Tool bars Lint 打开 lint  
工具  
代码区域  
IDE: Options Lint 设置对哪些项进行检  
Lint 工具可以对代码进行检查,并给出评  
分,建议修掉所有地方, 在Lint 报告输出  
的地方点击提示内容,会跳转到代码对应  
位置  
打开代码之后,点击  
Lint Current Tab,  
对代码进行检查  
检查报告,按照提示,  
修掉所有问题  
设置对哪些项进行检查  
2022/02/06  
Skill 教程  
87  
← 自定义一个函数,切换不同layer的显示  
1. procedure(LSWSwitch()  
2. hiDisplayMenu(  
← 显示menu  
3.  
hiCreateSimpleMenu(  
'popup  
← 创建simple menu  
menuHandle name,是个全局唯一的symbol  
menu的名字  
4.  
Menu 01  
所有的menu都需要一个menuHandle, 用于显  
示菜单、插入到菜单栏、或者弹出菜单.换个通俗  
的说法就是创建menu,实际包含了创建menu对  
象并且把它显示出来两个动作.  
5.  
"LSW"  
6.  
'("All Visible"  
"None Visible"  
7.  
包含4menu item list,  
每个stirng就是一个item  
8.  
"Routing Layer Visible"  
9.  
"Routing Layer Invisible")  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18.  
19.  
20.  
21.  
22.  
23.  
'("pteSetAllVisible(?panel \"Layers\")"  
"pteSetNoneVisible(?panel \"Layers\")"  
"pteShowUsedLPP(nil)  
除了”simple menu”, 其它所有menu对象都是一  
menu item的组合,也就是说创建menu对象实  
际包括了创建不同的menu item并把它们按照不  
同方式组合起来两个步骤  
pteSetNoneVisible(?panel \"Layers\")  
pteShowRoutingLPP(t)  
pteSetAllVisible(?panel \"Layers\")  
pteShowRoutingLPP(nil)  
list,与menu item对应的命令,  
每个字符串是一个(或一组)命令  
pteShowUsedLPP(t)"  
"pteShowUsedLPP(nil)  
pteSetAllVisible(?panel \"Layers\")  
pteShowRoutingLPP(t)  
pteSetNoneVisible(?panel \"Layers\")  
pteShowRoutingLPP(nil)  
pteShowUsedLPP(t)")  
24. ); hiCreateSimpleMenu  
25. ); hiDisplayMenu  
26. )  
↓ 设置成鼠标中键  
27. hiSetBindKey("Layout" "<Btn2Down>" "LSWSwitch()")  
2022/02/06  
Skill 教程  
88  
 
1. procedure(MyMenu(aaa)  
2. let((win seperator item1 item2 item3 item4)  
← 定义一个函数,供后续注册menu使用  
menu所属的window  
3. win=getCurrentWindow()  
Menu 02  
创建pulldown menu(下拉菜单)slider menu(滑  
menu的分割线  
4. seperator=hiCreateSeparatorMenuItem(?name 'seperator)  
5. item1=hiCreateMenuItem(?name 'item1 ?itemText "A1"  
6.  
7. item2=hiCreateMenuItem(?name 'item2 ?itemText "A2"  
8. ?callback "println(22)")  
9. pdmenu2=hiCreatePulldownMenu(  
?callback "println(11)")  
块菜单)  
创建2个  
menu item  
simple menu 不同的是,menu item需要用  
hiCreateMenuItem显示创建,调用命令也是通过  
callback(回调函数)来实现  
← 创建pulldown menu  
10.  
11.  
12.  
'pdmenu2  
slider menu并不直接调用函数,其通过子菜单的  
"Sample2"  
callback调用  
list(item1 item2))  
显示menu是通过deRegUserTriggers注册应用的  
13. slmenu=hiCreateSliderMenuItem(  
← 创建slider menu  
方式实现,这样打开每个window都会调用menu  
14.  
15.  
16.  
?name 'slmenu  
添加多个pulldown menu时注意先后顺序和添加  
?itemText "Slide Menu"  
?subMenu pdmenu2)  
的位置,避免覆盖  
slider menu调用了一个pulldown menu  
17. pdmenu1=hiCreatePulldownMenu(  
← 创建pulldown menu  
18.  
19.  
20.  
'pdmenu1  
"MyPullDown"  
↓ 各个menu item是按照这个list里的顺序排列  
list(item1 item2 seperator slmenu))  
21. hiInsertBannerMenu(win pdmenu1 hiGetNumMenus(win))  
↑ 将创建好的menu添加到win的菜单栏指定位置  
22. ))  
23. deRegUserTriggers("maskLayout" nil mask Layout 'MyMenu)  
24. deRegUserTriggers("maskLayoutXL" nil nil 'MyMenu)  
25. deRegUserTriggers("maskLayoutGXL" nil nil 'MyMenu)  
↑ 注册用户自己的API,这里就是MyMenu  
2022/02/06  
Skill 教程  
89  
1. procedure(BuildCustMenuItem()  
← 定义一个函数,供后续注册menu使用  
2.  
hiCreatePulldownMenu(  
'MyCustomMenu  
"Custom Menu"  
← 创建pulldown menu  
3.  
Menu 03  
在右键menu添加自定义menu item  
4.  
5.  
list(hiCreateMenuItem(  
?name 'item1  
6.  
mbRegisterCustomMenu 用于注册自  
7.  
?itemText "Item 1"  
定义右键菜单项  
8.  
?callback "println(\"Item 1\")")  
hiCreateMenuItem(  
创建2个  
menu item  
mbSetContextData 用于设置生效的  
对象,就是说,可以根据view type和  
选择的对象来控制自定义右键菜单是  
否生效  
9.  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18.  
19. )  
?name 'item2  
?itemText "Item 2"  
?callback "println(\"Item 2\")"))  
)
注意,不要直接用hiSetBindKey去设置左右键  
hiCreateSliderMenuItem(  
?name 'MySlider  
← 创建slider menu  
?itemText "Custom Menu"  
?subMenu MyCustomMenu  
)
slider menu调用pulldown menu  
↓ 定义一个函数,用于控制是否生效  
20. procedure(EnableCustMenuItem() t)  
21. mbRegisterCustomMenu("maskLayout" "custMenu"  
22.  
23. mbSetContextData("maskLayout" "custMenu"  
24. "Via Ruler" "Canvas" "Common")  
"EnableCustMenuItem()" "BuildCustMenuItem()")  
same ID  
↑ 设置context, 选中Via 或是 Ruler 时,自定义menu才有效,选中其它对  
象无效  
2022/02/06  
Skill 教程  
90  
Argument  
Value  
Schematic: schematicschematicXLanalogArtist-  
schematicadexl-schematicadegxl-schematic,  
hman-schematicamsArtist-schematic,  
mixedSignalArtist-schematicncVlogSchematic,  
spectre-schematicother-schematicUltraSim-  
schematicVHDLToolboxverilog,  
Menu 04  
mbSetContextData 参数如下:  
t_viewType  
mbSetContextData(  
t_viewType  
t_uniqueId  
schematicInteractivemspsSchematicApplication  
Layout: maskLayoutmaskLayoutParamCell,  
maskLayoutXLmaskLayoutGXLmaskLayoutCE,  
analogArtist-maskLayoutadexl-maskLayout,  
adegxl-maskLayoutother-maskLayoutspectre-  
maskLayoutUltraSim-maskLayout,  
maskLayoutIQViewparasitics-MaskLayout,  
verilogMaskLayoutmaskLayoutVSA  
InstanceNetShapePinViaGroupClone  
ModgenRulerMarkerFGuardRingNone  
PCellUngeneratedBoundaryBlockage,  
RowMosaicModInstRowRegionAny  
t_validObjs  
t_validWidgets  
t_grouping  
[ ?parent t_parent ]  
[ ?removeWhenDisabled g_removeWhenDisabled ]  
)
t_viewType  
t_validObjs  
其它还有创建 fixed menu(固定位置菜  
) tear-off menu(可分离菜单)的  
API,可查阅文档的关键字  
menus  
Menu Builder Functions  
t_validWidgets  
t_grouping  
Canvas Navigator  
CreateEditHierarchyGroupsObjSpecific,  
SecondaryCommonConstraintsand Properties  
2022/02/06  
91  
1. procedure(dbkStreamOut()  
2. let((cv gds_d)  
↓ 目录如果不存在,则创建,一般已经  
提前建好,所以这里注释掉  
3.  
gds_d= "../verify/gds/"  
实例 02  
4.  
;unless(isDir(gds_d) sh(strcat("mkdir -p " gds_d)))  
5.  
cv=geGetEditCellView()  
layout view 里直接一键导出GDS,完  
成下面几个步骤  
6.  
unless(cv~>cellViewType=="maskLayout"  
warn("%L %L %L is not layout view\n"  
7.  
CIW: File Export stream  
然后设置library/cell/view/gdspath  
点击确认  
8.  
cv~>libName cv~>cellName cv~>viewName)  
9.  
return(nil)  
↑ 根据view type 判断是否是layout view,  
如果不是则返回nil,后面不再执行  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18.  
19.  
20.  
21.  
)
xstSetField("virtualMemory" "true")  
xstSetField("library" cv->libName)  
xstSetField("ToPCell" cv->cellName)  
xstSetField("View" cv->viewName)  
← 设置为从内存中导出GDS,  
速度非常快(请注意是否已save)  
可以将dbkStreamOut绑定快捷键,在layout view  
里执行,这里不再演示;后面通过其它方式调用  
这个自定义函数  
xstSetField("strmFile" strcat(gds_d cv~>cellName ".gds"))  
xstSetField("replaceBusBitChar" "true")  
← 替换<>[ ]  
xstSetField("showCompletionMsgBox" "false")  
if(member("tsmcN16" ddGetLibList()->name) then  
xstSetField("enableColoring" "true")  
← 关闭执行完毕  
后的确认窗口  
根据是否存在“tsmcN16”这个library而选择是  
否打开 color选项,本版本不支持 color选项  
)
22. xstOutDoTranslate()  
← 执行strmOut  
23. ))  
2022/02/06  
Skill 教程  
92  
 
实例 03  
level 0  
这个实例解决了大家最常问到的一个问  
题,怎么抓取底层图形?  
右图示例是一个含有4 levellayout  
我们现在要在当前层level 0 抓取 F 的信息, 包  
F shape 本身以及它相对于当前层的transform  
transform 是一个包含了坐标、旋转、倍数  
list, 形如 list(10:20 “R90” 1.0)  
level 1  
然后将 F shape 拷贝到当前层  
我们先分析一下思路:  
1. 先确定我要抓取的区域,指定bBox即可  
2. 指定一层layer, 我们这里就是 M2:drawing  
3. 获取bBox内的所有M2:drawing shape ID  
4. 获取step3中每个shape相对于当前层的  
transform  
level 2  
5. 根据shape IDtransform 把每个shape 拷贝到  
当前层  
幸运的是,每一步Cadence都为我们提供了API,我  
们要做的就是把它们组合起来  
level 3  
2022/02/06  
Skill 教程  
93  
 
1. procedure(dbkShapeQuery(cv lpp bbox  
2. @optional (startLevel 0) (stopLevel 32))  
3. prog((objHier transList objList objTransList)  
实例 04  
直接上代码,这里我们定义了两个函数  
4.  
objHier=dbShapeQuery(cv lpp bbox startLevel stopLevel)  
↑ 内置API用于获取指定区域的shape  
5.  
transList=nil  
dbkShapeQuery 用于获取shapeID transform,  
返回一个list,在有些场景我们只需获取这个信息  
进行运算  
6.  
foreach(obj objHier  
7.  
transList=nconc(transLis  
← 把每个transfrom存到transList  
这个函数含有3个位置参数,分别用于指定  
cellviewlpp以及区域bbox  
8.  
list(dbGetHierPathTransform(obj))))  
↑ 内置API用于获取shapetransform  
还有两个可选参数,默认是抓取从 level 0  
9.  
objList=nil  
level 32  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18. ))  
foreach(obj objHier  
if(listp(obj) then  
dbkCopyFig 用于拷贝指定区域、指定的layer  
(下一页)  
objList=nconc(objList last(obj))  
↑↓ 把每个 shapeID 存到 objList  
objList=nconc(objList list(obj))  
这两个函数都可以独立使用  
else  
))  
objTransList=mapcar('list objList transList)  
← 返回值,供调用  
↓ 将shapeID transform 一一对应组合成一个list  
dbkShapeQuery 执行之后获取到  
这样一个list,每个元素又是一个  
shapeIDtransform组成的list  
return(objTransList)  
← 每个transform 都是相对于当前层的  
↓ 将函数直接当参数传进去 ↓  
19. ;;dbkShapeQuery(geGetEditCellView() leGetEntryLayer()  
geGetEditCellView()~>bBox)  
2022/02/06  
Skill 教程  
94  
1. procedure(dbkCopyFig(@optional  
2.  
3.  
(lpp leGetEntryLayer()) (startLevel 0)  
(stopLevel 32) (d_objects geGetSelSet()))  
实例 05  
4. prog((cv listNew objTransList newObj)  
5.  
cv=geGetEditCellView()  
if(cv~>mode=="r" then  
dbkCopyFig 用于拷贝指定区域、指定的  
layer  
← 注意,这个函数会对当前view进行编辑,要确  
6.  
mode edit,如果和readonly,则给出警告  
这个函数所有参数都是可选参数  
lpp 默认为当前LSW选中的layer  
startLevel stopLevel 分别为 0 32  
d_objects 为选中的对象,可以多选  
7.  
hiDisplayMenu(hiCreateSimpleMenu(  
'menu "" list(" read only ") list("")))  
8.  
9.  
else  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18.  
19.  
20. ))  
listNew=nil  
这个例子里我选中了右边的cell testv,按下快捷键D  
之后,能够看到在这个testv bbox 区域里的F shape  
都被拷贝到当前层了  
foreach(obj d_objects  
↓ 调用, 获取shapeIDtransform  
objTransList=dbkShapeQuery(cv lpp obj~>bBox  
你可以利用这两个函数做很多操作,例如把下一层  
label/pin 浮上到当前层,计算PCell各个layer 的坐  
标做自动连线,抽取pad location等等  
startLevel stopLevel)  
newObj=foreach(mapcar xx objTransList  
dbCopyFig(car(xx) cv cadr(xx)))  
← 将每个shape拷贝到当  
前层并赋值给  
listNew=nconc(listNew newObj)  
) ;foreach  
return(listNew)  
← 返回值,供调用  
) ;if  
21. hiSetBindKey("Layout" "Ctrl<Key>d" "dbkCopyFig()")  
2022/02/06  
Skill 教程  
95  
1. procedure(dbkWindowStatusCK(win)  
2. let((status)  
3.  
4.  
5.  
6.  
7.  
8.  
9.  
status=hiGetWindowState(win)  
↓ 判断查找的win的状态  
实例 06  
if(symbolToString(status)=="iconified" then  
action="hiDeiconifyWindow(window(%d))"  
↑ 如果是最小化,则恢复视图到最上层  
action="hiRaiseWindow(window(%d))"  
↑ 将window显示到最上层  
else  
dbkFindWindow 用于查找window  
有两处关键  
)
sprintf(nil action win~>windowNum)  
action根据window状态执行不同操作  
← 将winID传递给 action,  
注意这里返回的是一个string  
10.));procedure  
foreach-mapcar的用法,可以将其赋值打  
印出来  
11.procedure(dbkFindWindow()  
12.hiDisplayMenu(  
layout view里按下Ctrl+右键,会弹出window选择  
menu,当打开多个window时非常方便  
13. hiCreateSimpleMenu(  
14.  
'dbkFindWindowPopUpMenu  
"window"  
15.  
16.  
foreach(mapcar win hiGetWindowList()  
hiGetWindowName(win))  
← 获取window name list  
17.  
18.  
foreach(mapcar win hiGetWindowList()  
dbkWindowStatusCK(win))  
19.  
← 获取对应window name 需要执  
行的命令的list  
20. )  
21.));procedure  
22.hiSetBindKey("Layout" "Ctrl<Btn3Down>" "dbkFindWindow()")  
23.hiSetBindKey("Schematics" "Ctrl<Btn3Down>" "dbkFindWindow()")  
2022/02/06  
Skill 教程  
96  
 
1. procedure(dbkAddPrefixSuffixToAllInLib(  
2. s_library s_prefix @optional (s_suffix ""))  
3. let((libraryID src newName des replaced_cell)  
实例 07  
dbkAddPrefixSuffixToAllInLib 用于给指  
定的library里所有cell加前缀或是后缀  
← 获取libraryID  
4.  
5.  
6.  
libraryID=ddGetObj(s_library)  
replaced_cell=list()  
foreach(cellname libraryID~>cells~>name  
这里用到两个内置API  
← 遍历library所有cell  
替换前  
gdmCreateSpec 创建一个gdmSpec 对象,  
用于设计数据的管理,例如重命名、拷贝、  
版本管理等等  
7.  
src=gdmCreateSpec(s_library cellname "" "" "CDBA")  
newName=strcat(s_prefix cellname s_suffix)  
信息 →  
8.  
替换后  
ccpRename 用于rename librarycell、  
9.  
des=gdmCreateSpec(s_library newName "" "" "CDBA")  
ccpRename(src des t 'CCP_EXPAND_COMANAGED  
信息 →  
view  
详细用法请查阅文档  
10.  
执行替换 ↑  
执行之后的效果如下图,调用关系也一同修改,这  
里未做显示  
11.  
'CCP_UPDATE_DESTLIB_ONLY)  
replaced_cell=cons(sprintf(nil "%32s %-32s  
cellname newName) replaced_cell)  
12.  
13.  
打印log  
14.  
);foreach  
if(replaced_cell foreach(x replaced_cell printf("%s\n" x)))  
15.  
16. ))  
17.dbkDeletePrefixSuffixToAllInLib("TEST" "pre_")  
18. ;dbkDeletePrefixSuffixToAllInLib(“TEST" "" "_suf")  
19. ;dbkDeletePrefixSuffixToAllInLib(“TEST" "pre_" "_suf")  
添加前缀、后缀、前后缀  
2022/02/06  
Skill 教程  
97  
 
1.  
2.  
3.  
4.  
procedure(dbkDeletePrefixSuffixToAllInLib(  
s_library s_prefix @optional (s_suffix ""))  
let((libraryID src newName des s_index r_index replaced_cell)  
libraryID=ddGetObj(s_library)  
实例 08  
5. replaced_cell=list()  
6.  
foreach(cellname libraryID~>cells~>name  
s_index=nindex(cellname s_prefix)  
r_index=rindex(cellname s_suffix)  
7.  
dbkDeletePrefixSuffixToAllInLib用于给指  
定的library里所有cell删除前缀或是后缀.  
8.  
9.  
src=gdmCreateSpec(s_library cellname "" "" "CDBA")  
cond(  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18.  
19.  
20.  
21.  
22.  
23.  
24.  
25.  
(s_index==1 && r_index  
执行之后的效果如下图, 调用关系也一同修改,这  
里未做显示  
newName=substring(cellname length(s_prefix)+1  
length(cellname))  
newName=substring(newName 1 length(newName)-length(s_suffix)))  
(s_index==1  
newName=substring(cellname length(s_prefix)+1  
length(cellname)))  
(r_index  
newName=substring(cellname 1 length(cellname)-length(s_suffix)))  
) ;cond  
if(s_index==1 || r_index then  
des=gdmCreateSpec(s_library newName "" "" "CDBA")  
ccpRename(src des t 'CCP_EXPAND_COMANAGED 'CCP_UPDATE_DESTLIB_ONLY)  
replaced_cell=cons(sprintf(nil "%32s %-32s" cellname newName)  
replaced_cell))  
26. ) ;foreach  
27. if(replaced_cell foreach(x replaced_cell printf("%s\n" x)))  
28. ))  
29. dbkDeletePrefixSuffixToAllInLib("TEST" "pre_")  
30. ;dbkDeletePrefixSuffixToAllInLib(“TEST" "" "_suf")  
31. ;dbkDeletePrefixSuffixToAllInLib(“TEST" "pre_" "_suf")  
2022/02/06  
Skill 教程  
98  
1. procedure(dbkAllInOne()  
2. let(()  
3. unless(boundp(‘myAppForm)  
4.  
5.  
oneBox=hiCreateStringField(  
?name 'oneBox  
← 创建一个stringFiled(单行输入框)  
实例 09  
6.  
7.  
8.  
?prompt "Input"  
?defValue "nil"  
← 输入框的默认值  
)
9.  
myAppForm=hiCreateAppForm(  
?name ‘myAppForm  
?formTitle "DBK All In One"  
?fields list(oneBox)  
?callback "dbkCB(oneBox)"  
?buttonLayout 'OKCancel  
)
← 创建form  
创建一个单行输入框来模拟 CIW, 在任  
意一个layout view 里按下g调出输入框  
分别输入 c r 来查看CIW里的输出  
输入 so ,调用之前的自定义函数导出gds  
输入 (1+2)*3  
10.  
11.  
12.  
13.  
14.  
15.  
16. )  
list的元素是filed,这里只有一个filed  
← 这个form引用的回调函数  
17. hiDisplayForm(‘myAppForm)  
18. )); procedure dbkAllInOne  
← 显示form  
输入 sqrt(16)  
19.  
procedure(dbkCB(text)  
20. let((cmdString cmd)  
← 创建一个函数供调用  
输入 geGetEditCellView()~>libName  
输入 pwd()  
21. cmdString=parseString(text~>value)  
22. cmd=car(cmdString)  
23. cond(  
text~>value 是一个string  
← 提取输入内容的第一个单词  
通过自定义一套命令语法,可以很便捷的执行一些  
列命令,请自己发挥  
24.  
25.  
26.  
27.  
28.  
29.  
30.  
31.  
32.  
33.  
34.  
35.  
36.  
("c"==cmd  
printf("C>>>%L %L\n" text~>value text~>lastValue)  
)
↑ 获取当前值  
("r"==cmd  
printf("R>>>%L %L\n" text~>value text~>lastValue)  
)
↑ 获取上一次的值  
("so"==cmd  
dbkStreamOut()  
)
(t  
println(evalstring(text~>value))  
)
↑ 如果解析第一个单词不是上面任何一种情  
况,则把整个字符串当做一个命令执行  
)
37. )); procedure dbkCB  
38. hiSetBindKey("Layout" "<Key>g" "dbkAllInOne()")  
2022/02/06  
Skill 教程  
99  
 
1. procedure(myComboForm()  
2. let(()  
3. myInt = hiCreateIntField(  
← 创建一个intFiled(只能输入整数)  
4.  
5.  
6.  
7.  
8.  
?name 'countApple  
?prompt "Count of apple (0..5)"  
?value 2  
?defValue 1  
?range '(0 5) )  
示例 04  
← 限定输入范围  
创建一个form, 包含以下field:  
intField  
9. myCyclic = hiCreateCyclicField(  
10. ?name 'color  
← 创建一个cyclicFiled(下拉选择)  
11. ?prompt "Color"  
12. ?value "red"  
13. ?choices list("red" "black" "yellow" "green" "blue" "white") )  
14. mytoggle = hiCreateToggleField(  
15. ?name 'fruit  
16. ?prompt "what fruit do you like?"  
17. ?choices list(  
cyclicField  
toggleField  
← 创建一个toggleFiled(复选)  
radioField  
获取field值,并打印出来  
18.  
19.  
20.  
'(wCream "apple")  
list('nuts "banana")  
'(peach))  
示例效果如下图  
21. ?value '(t t nil)  
22. ?numSelect 3 )  
← 复选项初始状态  
23. myRadio = hiCreateRadioField(  
24. ?name 'size  
← 创建一个radioFiled(单选)  
25. ?prompt "Size"  
26. ?value "Large"  
27. ?defValue "Small"  
← 单选项初始状态  
28. ?choices list("Small" "Medium" "Large" ))  
29. hiCreateAppForm( ?name 'myCreamForm  
30. ?formTitle "Super Market"  
31. ?callback "printf(\"%s and %s\"  
← 获取Field值  
32.  
myCyclic~>value myRadio~>value)"  
33. ?fields list(myInt myCyclic mytoggle myRadio )  
34. ?help "cream" )  
Filed排列是一维自上而下排列  
35. hiDisplayForm('myCreamForm)  
36. ))  
← 将创建的form显示出来  
37. hiSetBindKey("Layout" "<Key>h" "myComboForm()")  
2022/02/06  
Skill 教程  
100  
示例 05  
修改PCell的属性值  
CDF参数名→  
以右边nch_mac这个layout PCell 为例,先选中,  
然后在属性窗口里勾选 Display CDF Parameter  
Name  
在修改PCell属性的时候,需要知道CDF参数是否  
callback函数. 一般foundry PCellCDF参数通  
callback函数修改,经常会有多个属性联动,  
即改变一个属性,另外一个或几个属性也一同改  
勾选→  
以右边nch_mac为例,w=Wfg*fingers,当在属性  
← 正常  
窗口里修改fingers=4时,w1.6u  
可以通过 vfoSetParam(car(geGetSelSet())  
“fingers” “4”) 这种方式修改,会触发callback函数,  
能够看到与在属性窗口里修改是一样的  
如果通过 car(geGetSelSet())~>fingers=2 这种方  
式修该,能够看到fingers=4, 但w还是800n,容  
易导致不易察觉的错误;如果够确认没有调用  
callback函数联动其它属性修改,这种方式也可  
以使用,自己写的PCell普通参数也可以通过这种  
方式修改  
← 异常  
2022/02/06  
Skill 教程  
101  
 
26. ;bottom  
27. col_r=col-col/2  
28. xy= 0:-10  
pcCellView 是仅用于PCell里的特殊变量,  
用于表示PCell所在的cellview  
29. dbCreateSimpleMosaic(pcCellView filler_line nil  
实例 10-PCell  
30.  
xy "R0" 1 col_r 10 20)  
↓ 首先,创建两个cellfiller corner  
;manually create filler and corner cell whose bBox is (0:0 10:10)  
31. col_r=col/2  
32. if((col_r*2 == col) then  
1.  
bottom side调用filler cell  
top side调用filler cell  
left side调用filler cell  
33.  
34.  
35.  
xy=col*10:-10  
else  
xy=(col-1)*10:-10)  
2.  
plib="TEST_PCell" PCell="io_ring" pview="layout"  
3.  
pcDefinePCell(  
pcDefinePCell 用于定义PCell  
4.  
list(ddGetObj(plib) PCell pview)  
plib事先创建好,然后  
在这里定义PCelllibrary、  
cellnameview  
36. dbCreateSimpleMosaic(pcCellView filler_line nil  
37.  
38. ;top  
39. col_r=col-col/2  
40. xy= 0:row*10  
41. dbCreateSimpleMosaic(pcCellView filler_line nil  
42.  
43. col_r=col/2  
44. if((col_r*2 == col) then  
45.  
46.  
47.  
48. dbCreateSimpleMosaic(pcCellView filler_line nil  
49.  
50. ;left  
51. row_r=row-row/2  
52. xy= -10:0  
53. dbCreateSimpleMosaic(pcCellView filler_line nil  
54.  
55. row_r=row/2  
56. if((row_r*2 == row) then  
57.  
58.  
59.  
5.  
(
xy "MY" 1 col_r 10 20)  
6.  
(row int 2)  
(col int 3)  
7.  
8.  
(corner string "corner")  
(filler string "filler")  
(both string "filler")  
)
定义PCell的参数列表,注意  
这个list前面没有关键字list,  
参数名也不需要是个symbol,  
不要“”  
9.  
xy "R0" 1 col_r 10 20)  
10.  
11.  
12. let((xy filler_line col_r row_r cell_id cdf_id)  
13. ;corner  
xy=col*10:row*10  
else  
xy=(col-1)*10:row*10)  
let部分创建PCell 实体  
14.  
15.  
16.  
17.  
xy=-10:-10  
dbCreateInstByMasterName(pcCellView plib  
corner pview nil xy "R0" 1)  
xy "MY" 1 col_r 10 20)  
xy=(col+1)*10:-10  
18.  
19.  
20.  
dbCreateInstByMasterName(pcCellView plib  
corner pview nil xy "R90" 1)  
xy "R0" row_r 1 20 10)  
4个角调用corner cell  
xy=(col+1)*10:(row+1) *10  
21.  
22.  
23.  
dbCreateInstByMasterName(pcCellView plib  
corner pview nil xy "R180" 1)  
xy=-10:row*10  
else  
xy=-10:(row-1)*10)  
xy=-10:(row+1) *10  
24.  
25.  
dbCreateInstByMasterName(pcCellView plib  
corner pview nil xy "R270" 1)  
2022/02/06  
60. dbCreateSimpleMosaic(pcCellView filler_line nil  
61.  
xy "MX" row_r 1 20 10)  
Skill 教程  
102  
 
88. cdfCreateParam(cdf_id  
89.  
90.  
91.  
92.  
93.  
?name "col"  
CDF 参数name,与5~11行的对应  
?prompt "count of col"  
?type "int"  
实例 11-PCell  
63. row_r=row-row/2  
64. xy= col*10:0  
?defValue 3  
?editable "t")  
62. ;right  
94. cdfCreateParam(cdf_id  
95.  
96.  
97.  
98.  
99.  
100.  
?name "filler"  
?prompt "filler cell"  
?type "string"  
65. dbCreateSimpleMosaic(pcCellView filler_line nil  
66.  
xy "R0" row_r 1 20 10)  
67. row_r=row/2  
68. if((row_r*2 == row) then  
?defValue "filler"  
?editable "t"  
right side调用filler cell  
69.  
70.  
71.  
xy=col*10:row*10  
else  
xy=col*10:(row-1)*10)  
?callback "io_ring_CB('filler)")  
← 创建CDF 参数,这个有callback函数,  
当属性form里有变动时,会触发callback函  
101. cdfCreateParam(cdf_id  
102.  
103.  
104.  
105.  
106.  
107.  
?name "corner"  
72. dbCreateSimpleMosaic(pcCellView filler_line nil  
?prompt "corner cell"  
73.  
74.  
xy "MX" row_r 1 20 10)  
?type "string"  
?defValue "corner"  
?editable "t"  
type string” 是一个string field,通过输入值修  
cell_id=ddGetObj(plib PCell)  
75. unless(cell_id error("cannot get PCell %s" PCell))  
← 获取当前PCellcell ID  
76. cdf_id=cdfGetBaseCellCDF(cell_id)  
77.  
← 获取当前PCellcdf ID  
?callback "io_ring_CB('corner)")  
108. cdfCreateParam(cdf_id  
78. if(cdf_id cdfDeleteCDF(cdf_id))  
79. cdf_id=cdfCreateBaseCellCDF(cell_id)  
80.  
删除已有的CDF,重新创建  
新的CDF.如果是创建简单的  
PCell,从74~116行都不需要  
109.  
110.  
111.  
112.  
113.  
114.  
115.  
?name "both"  
?prompt "both cell"  
?type "radio"  
type radio” 是一个radio field,通过单选修改  
81. cdfCreateParam(cdf_id  
?defValue "default"  
?choices list("default" "inverse")  
?editable "t"  
82.  
83.  
84.  
85.  
86.  
87.  
?name "row"  
?prompt "count of row"  
?type "int"  
?defValue 2  
?display "t"  
← 单选项列表  
?callback "io_ring_CB('both)")  
← 同样,当单选框变动时触发callback函  
116. cdfSaveCDF(cdf_id)  
117.)); pcDefinePCell  
?editable "t")  
← 创建CDF 参数,这个没有callback函数  
2022/02/06  
Skill 教程  
103  
实例 12-PCell  
118.procedure(io_ring_CB(key)  
← 创建一个callback 函数  
119.let(()  
120. case(key  
121.  
122.  
123.  
124.  
125.  
126.  
127.  
128.  
129.  
130.  
131.  
132.  
133.  
134.  
135.  
(both  
← 当CDF name both 时触发  
if(cdfgData~>both~>value=="default" then  
cdfgData~>filler~>value=cdfgData~>filler~>defValue  
cdfgData~>corner~>value=cdfgData~>corner~>defValue  
else  
~>defValue 获取前面定义的default值 ↓  
cdfgData~>filler~>value=cdfgData~>corner~>defValue  
cdfgData~>corner~>value=cdfgData~>filler~>defValue  
cdfgData, 又一个仅用于PCell里的特  
殊变量,用于表示cdf ID,通过~>操作符  
获取CDF参数的值  
)
);both  
(corner  
cdfgData~>corner~>value=cdfgData~>corner~>value  
~>value 获取form里当前值  
);corner  
(filler  
cdfgData~>filler~>value=cdfgData~>filler~>value  
);filler  
136. ); case key  
137.)); procedure io_ring_CB  
2022/02/06  
Skill 教程  
104  
实例 13-PCell  
修改PCell的属性值  
上述代码展示了如何创建一个io_ringPCell  
首先创建library TEST_PCell,专门用于放置PCell  
然后手动创建好filler corner 两个cell,大小都  
10*10  
将上面代码保存到myPCell.il, 然后在CIWload,  
就可以看到创建好的PCell  
本示例是通过调用其它cell来创建PCell,你也可  
以完全通过创建图形来写一个PCell,例如MOS  
当你重新启动Virtuoso时,你会发现PCell Failed,  
那是因为myPCell.il需要重新load.你可以把  
myPCell.il放到TEST_PCell目录下,并同时创建一  
libInit.il , 内容如下  
load “myPCell.il”  
这样当library被触发时,会自动load libInit.il.需要  
注意的是,libInit.il 只会触发一次,如果做了修  
改要重启再次触发生效  
2022/02/06  
Skill 教程  
105  
注意 PCell  
创建Pcell的安全规则  
概括的说只能使用右图所列的函数,不能使用图  
形化相关的API,例如lege等开头的API,否则  
会在导出GDS时,报Pcell Eval Failed 错误;也不  
能使用printf 或是 println, 否则打印输出会把当做  
error处理  
详细规则,请查阅文档中关于Pcell创建的一些注  
意事项 Safety Rules for Creating SKILL Pcells  
2022/02/06  
Skill 教程  
106  
实例 14  
通过脚本的方式导出GDS  
以右图 TEST_A/inv_x1 为例  
新建一个文件auto_gds_out.csh, 将右边代码写  
入并保存  
然后 chmod +x auto_gds_out.csh 将文件修改为  
← 必须在第一行  
$1 脚本后跟的第一个参数  
$2 脚本后跟的第二个参数  
1. #!/bin/csh -f  
2. set library = "$1"  
3. set cell = "$2"  
4. strmout \  
可执行  
cds.lib 拷贝或者link 到脚本的相同目录下  
调用 strmout 命令导出GDS,  
5.  
6.  
-library $library \  
-toPCell $cell \  
terminal 里执行即可导出GDS  
注意换行符 \ 后面不能有任  
何字符(包括空格), 中间也  
不能有空行隔开  
./auto_gds_out.csh TEST_A inv_x1  
7.  
-view layout \  
8.  
9.  
10.  
11.  
12.  
-strmFile $cell.gds \  
-outputDir ../verify/gds \  
-convertDot node \  
-case preserve \  
strmout 命令的具体用法输入strmout h 查看  
也可以先在GUI里导出一次GDS,然后在CIW里  
查找输出,看看GUI导出时实际执行的命令  
-logFile gdsout.log  
INFO (XSTRM-222): strmout -library 'TEST_A' -strmFile  
'/home/work/cadence/inv_x1.gds' -techLib 'N28' -toPCell  
'inv_x1' -view 'layout' -logFile 'strmOut.log’  
$status 特殊变量,表示上一个命令  
13. if($status) then  
14.  
15. else  
16.  
echo strmout $cell.gds failed!  
返回的状态,0 为  
执行正常,非0为异常  
Virtuoso Framework License (111) was checked out  
successfully. Total checkout time was 0.04s.  
echo strmout $cell.gds successfully!  
17. endif  
2022/02/06  
Skill 教程  
107  
 
1. #!/bin/csh -f  
2. set library = "$1"  
3. set cell = "$2"  
← 必须在第一行  
cat <<EOF >! 强制输出到文件  
4. cat <<EOF >! si.env  
5. simLibName = "$library"  
6. simCellName = "$cell"  
实例 15  
7. simViewName = "schematic"  
8. simSimulator = "auCdl"  
9. simNotIncremental = 't  
10. simReNetlistAll = nil  
11. simViewList = '("auCdl" "schematic")  
12. simStopList = '("auCdl")  
13. hnlNetlistFileName = "$cell.cdl"  
14. resistorModel = ""  
通过脚本的方式导出CDL  
还是以TEST_A/inv_x1 为例  
新建一个文件auto_cdl_out.csh, 将右边代码写  
15. shortRES = 2000.0  
16. preserveRES = 't  
17. checkRESVAL = 't  
入并保存  
18. checkRESSIZE = 'nil  
19. preserveCAP = 't  
20. checkCAPVAL = 't  
然后 chmod +x auto_cdl_out.csh 将文件修改为  
可执行  
21. checkCAPAREA = 'nil  
22. preserveDIO = 't  
23. checkDIOAREA = 't  
24. checkDIOPERI = 't  
25. checkCAPPERI = 'nil  
26. simPrintInhConnAttributes = 'nil  
27. checkScale = "meter"  
28. checkLDD = 'nil  
将这段内容写入到 si.env  
cds.lib 拷贝或者link 到脚本的相同目录下  
terminal 里执行即可导出GDS  
./auto_cdl_out.csh TEST_A inv_x1  
si 命令的具体用法输入si h 查看  
29. pinMAP = 'nil  
30. preserveBangInNetlist = 'nil  
31. shrinkFACTOR = 0.0  
32. globalPowerSig = ""  
33. globalGndSig = ""  
也可以先在GUI里导出一次CDL,然后查看目录  
下的si.env 文件,修改右边的代码  
34. displayPININFO = 't  
35. preserveALL = 't  
36. setEQUIV = ""  
37. incFILE = "/home/pdk/foundry/N28/../Calibre/lvs/source.added"  
38. auCdlDefNetlistProc = "ansCdlSubcktCall"  
39. EOF  
EOF作为标识符,与前面的EOF对应  
40. cat /dev/null >! netlist  
41. si -batch -command netlist  
Skill 教程  
← 执行 si 命令导出cdl 网表  
2022/02/06  
108  
 
实例 16  
通过脚本的方式跑LVS  
还是以TEST_A/inv_x1 为例  
1. #!/bin/csh -f  
2. set cell = "$1"  
← 必须在第一行  
新建一个文件auto_run_lvs.csh, 将右边代码写入  
并保存  
3. cp calibre.lvs_ori calibre.lvs  
将文件中的 lvs_top  
← 替换为$cell  
然后 chmod +x auto_run_lvs.csh 将文件修改为可  
4. sed -i "s/lvs_top/$cell/g" calibre.lvs  
5. calibre -lvs -hier -hyper -turbo 2 calibre.lvs  
6. echo calibre -lvs -hier -hyper -turbo 2 calibre.lvs  
执行  
calibre.lvs deck 备份为calibre.lvs_ori  
echo 输出执行的命令  
terminal 里执行即可以command 方式跑lvs  
./auto_run_lvs.csh inv_x1  
DRC的方式类似  
2022/02/06  
Skill 教程  
109  
 
实例 17  
通过脚本的方式跑一个完整验证流程  
还是以TEST_A/inv_x1 为例  
新建一个文件auto_flow.csh, 将右边代码写入并保  
然后 chmod +x auto_flow.csh 将文件修改为可执行  
terminal 里执行即可以command方式依次导出  
cdlgds,然后再跑lvsdrc. Nice!  
./auto_flow.csh TEST inv_x1  
如果要跑多个cell的验证,只需要在包一层做个循  
环即可,这里不再演示  
注意,这里没有做异常处理,就是说,前面的命令  
执行失败了,后面的命令也可能会继续执行  
这里涉及到的几个知识点  
cshell 编程请查看  
sed 命令请查看  
2022/02/06  
Skill 教程  
110  
 
1. procedure(dbkHighlightNetInSchematic()  
2. prog((cv win objs nets)  
实例 18  
3.  
cv=geGetEditCellView()  
4.  
unless(cv~>cellViewType=="schematic"  
← 只对schematic view 进行操作  
dbkHighlightNetInSchematic 用于电路  
5.  
warn("not a schematic view\n")  
图里高亮所选instance 的所有连线  
6.  
return(nil)  
7.  
)
8.  
win=getCurrentWindow()  
9.  
objs=setof(x geGetSelSet() x~>conns)  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18.  
19.  
20.  
21. ))  
nets=list()  
if(objs then  
foreach(x objs~>conns~>net~>name  
nets=append(nets x)  
)
else  
nets=cv~>nets~>name  
)
foreach(net nets  
geAddNetProbe(win nil net)  
)
22. hiSetBindKey("Layout" "<Key>3" "dbkHighlightNetInSchematic()")  
2022/02/06  
Skill 教程  
111  
 
1. procedure(dbkSwitchBindKey()  
2. /*--DOC--  
43.  
44.  
45.  
46.  
47.  
48.  
49.  
50.  
51.  
52.  
53.  
54.  
55.  
56.  
57.  
58.  
59.  
60.  
61.  
62.  
63.  
64.  
65.  
66.  
67.  
68.  
69.  
70.  
71.  
72.  
73.  
74.  
(gv~>bindKey=="custom" || t  
defaultBindKey1="./defaultBindKey.il"  
3. API:  
defaultBindKey2="~/defaultBindKey.il"  
4. dbkSwitchBindKey()  
cond(  
实例 19  
5. => t / nil  
(isFile(defaultBindKey1)  
6. Description: Swtich between defaultBindKey and myBindKey  
7. Arguments:  
load(defaultBindKey1)  
hiSetBindKey( "Layout" "<Key>Tab" "dbkSwitchBindKey()")  
8. Value Returned:  
hiSetBindKey( "Schematics" "<Key>Tab" "dbkSwitchBindKey()")  
9.  
t
Swtich between defaultBindKey and myBindKey successfully.  
defaultBindKey.il locats at . or ~  
gv~>bindKey="default"  
10.  
11.  
12.  
info("set bindKey to default")  
myBindKey.il locats at . or ~  
)
if foundwill load it  
(isFile(defaultBindKey2)  
13. nil can't find defaultBindKey.il or myBindKey.il or can't find variable gv  
14. Examples:  
load(defaultBindKey2)  
hiSetBindKey( "Layout" "<Key>Tab" "dbkSwitchBindKey()")  
15. dbkSwitchBindKey()  
hiSetBindKey( "Schematics" "<Key>Tab" "dbkSwitchBindKey()")  
16. */;--ENDDOC--  
gv~>bindKey="default"  
17. prog((myBindKey1 myBindKey2 defaultBindKey1 defaultBindKey2)  
info("set bindKey to default")  
18.  
19.  
20.  
21.  
22.  
23.  
24.  
25.  
26.  
27.  
28.  
29.  
30.  
31.  
32.  
33.  
34.  
35.  
36.  
37.  
38.  
39.  
40.  
41.  
42.  
unless(boundp('gv)  
)
warn("can't find variable gv")  
(t  
return(nil))  
warn("can't access defaultBindKey.il")  
unless(gv~>bindKey gv~>bindKey="default")  
return(nil)  
cond(  
)
(gv~>bindKey=="default"  
myBindKey1="./myBindKey.il"  
myBindKey2="~/myBindKey.il"  
cond(  
); custom  
)
;(t  
;
gv~>bindKey="default"  
(isFile(myBindKey1)  
load(myBindKey1)  
gv~>bindKey="custom"  
info("set bindKey to custom")  
)
;)  
);cond  
t
))  
hiSetBindKey( "Layout" "<Key>Tab" "dbkSwitchBindKey()")  
hiSetBindKey( "Schematics" "<Key>Tab" "dbkSwitchBindKey()")  
(isFile(myBindKey2)  
load(myBindKey2)  
gv~>bindKey="custom"  
info("set bindKey to custom")  
)
通过Tab键切换两套Bindkey设置  
gv 是一个defstruct, 在数据结构里有介绍,需要事先创建  
通过记录状态 gv~>bindKey 来循环切换  
(t  
warn("can't access myBindKey.il")  
return(nil)  
这是一个较规范的脚本实例,通过注释说明函数的使用方法  
)
)
2022/02/06  
);default  
Skill 教程  
112  
 
一些经验  
写脚本的套路,  
打开工具的log输出,所有EDA工具的任何操作都有对应的API  
log里的API提取出来进行组合就是一个脚本  
先学会修改,再自己写  
先实现功能,再优化性能  
相同作用的函数可能有多个, 选个最简洁的  
如果没有循环、迭代语句,几乎不用考虑性能问题  
递归(就是函数自己调用自己)可能导致性能低下,需要仔细优化  
GUI操作时,先判断mode  
脚本可以在readonly下操作  
PCell可以先用普通函数开发,然后转为pcDefinePCell  
有些API不能用在PCell里, 多看看log  
仿真相关的脚本使用ocean(Skill子集)开发  
** 我的环境缺少库无法仿真, 所以没有示例。在ADE:filesave script 可以保存一个脚本,自己研究吧  
2022/02/06  
Skill 教程  
113  
其它  
ipcXXX 进程间通讯,即调用外部命令  
techXXX techfile处理  
rodXXX rod图形创建  
Fluid Guardring  
2022/02/06  
Skill 教程  
114  
谢谢  
2022/02/06  
Skill 教程  
115