Uploaded by 仲咩

Skill教程

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