https://www.cnblogs.com/yeungchie/
寫個大筆記,低速更新中 ...
Cadence Skill
Cadence 提供二次開發的 SKILL 語言,它是一種基於通用人工智能語言— Lisp 的交互式高級編程語言 ①。
SKILL 語言支持一套類似 C 語言的語法,大大降低了初學者學習的難度,同時高水平的編程者可以選擇使用類似 Lisp 語言的全部功能。所以 SKILL 語言既可以用作最簡單的工具語言,也可以作為開發任何應用的、強大的編程語言。
SKILL 可以與底層系統交互,也提供了訪問 Cadence 各個工具的豐富接口。用戶可以通過 Skill 語言來訪問,並且可以開發自己的基於 Cadence 平台的工具。
① LISP 即 List Processing-表處理,是最早和最重要的符號處理編程語言之一,它於1958年由美國的 J. McCarthy 提出,LISP 在人工智能AI方面獲得廣泛應用。
我的環境是 Virtuoso IC618 平台,可能存在某些特性不適用於舊版本。
如何查看官方資料
cdsFinder
- 用於模糊查找 Skill 函數,查看簡單介紹。
cdsFinder &
# $CDSHOME/tools/bin/cdsFinder
cdnshelp
- 用於查看更加詳細的內容,軟件的使用手冊。
cdnshelp &
# $CDSHOME/tools/bin/cdnshelp
代碼風格
語法風格
由於 Skill 語言是基於 Lisp ,因此它支持 函數表示法
和 前綴表示法
來書寫代碼。
-
函數表示法
func( arg1 arg2 )
-
前綴表示法
( func arg1 arg2 )
以上的兩種表示法可以同時存在,因此在編寫代碼時候最好注意格式的統一。
推薦統一使用 函數表示法
,並需要注意函數名和括號之間不能有空格。
錯誤寫法 :
func ( arg1 arg2 )
func
后面多了一個空格,這會將arg1
作為函數名
命名風格
其次函數和變量的命名風格,Skill 中一般是使用 駝峰命名法 ( Camel-Case ),函數名中的每一個邏輯斷點(單詞)首字母大寫,開頭第一個單詞全部小寫,一般代表 函數/變量 的類別、作者,像我寫的函數和全局變量一般都習慣用 yc
開頭。
這是官方使用的命名方式,也是現在流行的一種命名方式。
當然如何選擇取決於你自己。
下面是一個對比
-
駝峰命名法
geGetEditCellView() dbOpenCellViewByType(libName cellName viewName nil mode)
-
蛇形命名法(下划線)
不推薦。
ge_get_edit_cell_view() db_open_cell_view_by_type(lib_name cell_name view_name nil mode)
最后再注意命名不能過於簡化,例如 a
,b
,c
。命名的目標是至少讓人能一眼看出來這個變量是做什么用的(一眼不行看兩眼),例如上面的 libName
,cellName
,viewName
。
下面介紹語法部分我會使用的一些非常簡化變量命名,因為只是用於演示,命名就會比較隨意了。
基礎語法
hello world
舉例一種 hello world 的寫法。
println( "hello world" )
; "hello world"
注釋
- 單行注釋
; 這是單行注釋
- 多行注釋
/*
這是
多行注釋
*/
真 / 假
Skill 中 真用 t
來表示,假用 nil
來表示。
其實參與判斷中的值,除了 nil
和空鏈表以外都可以認為是真,例如:
-
判斷
t
when( t println( "True" ) ) ; t 為真,打印 "True"
-
判斷
10
when( 10 println( "True" ) ) ; 10 為真,打印 "True"
-
判斷
"YEUNGCHIE"
when( "YEUNGCHIE" println( "True" ) ) ; "YEUNGCHIE" 為真,打印 "True"
-
判斷
nil
when( nil println( "True" ) ) ; nil 為假,不運行 println 語句
when 語句中第一個參數為判斷條件,條件為真才會運行。
數據類型
可以用函數 type
查看一個數據的類型標識。
type( 'YEUNGCHIE ) ; symbol
type( "YEUNGCHIE" ) ; string
type( list( "YEUNGCHIE" )) ; list
用 println
打印一個數據的內容,同時也能夠從打印結果觀察到數據類型。
println( 'YEUNGCHIE ) ; YEUNGCHIE
println( "YEUNGCHIE" ) ; "YEUNGCHIE"
println( list( "YEUNGCHIE" )) ; ( YEUNGCHIE )
數據類型標識
官方的教程中一般會用來表明函數需要的輸入數據是什么類型的( 標在前綴 )。也用於限制子函數輸入數據的類型。
前綴 | 內部命名 | 數據類型 |
---|---|---|
d | dbobject | id , Cadence 數據對象 |
x | integer | 整數 |
f | flonum | 浮點數 |
n | number | 整數 或者 浮點數 |
g | general | 通用的 , 任何數據類型 |
l | list | 鏈表 |
p | port | I / O 句柄 |
t | string | 字符串 |
s | symbol | " 符號 " |
S | stringSymbol | " 符號 " 或者 字符串 |
u | function | 函數對象 , 函數名 或者 lambda對象 |
... | ... | ... |
字符串
- 定義方式
字符串用雙引號括起來。
"YEUNGCHIE"
; "YEUNGCHIE"
-
相關函數
-
strcat 字符串連接
a = "YEUNG" b = "CHIE" c = strcat( a b ) println( c ) ; "YEUNGCHIE"
-
strlen 字符串長度
下面的 c 沿用上面的變量 c,為了演示不寫得過於繁雜、重復。
println(strlen( c )) ; 9
-
數字
數字分為 整數 和 浮點數。
-
整數
18
也可以直接編寫成 二進制 (
0b
前綴 )、八進制 (0
前綴 )、十六進制 (0x
前綴 ),但默認會輸出成 十進制0b10010 ; 18 024 ; 20 0xFE ; 254
-
浮點數
3.14
浮點數也可以使用 科學計數法 和 單位后綴 來表示
1e-06 ; 0.000001 1u ; 0.000001
symbol(符號)
symbol 的寫法是用一個單引號開頭,例如:'a
、'b
、'c
,這個類型比較抽象,知道有這么個東西就行,重點是需要用到的時候知道如何去使用。
symbol 實際上是一種指針。
每個 symbol 都存在以下幾個組成部分(slot):
- Print name ( name )
- Value
- Function binding
- Property list
只有 name 是必要的,其他的部分都可以為空,當一個 文本引用 產生時,系統會創建一個 symbol ,且 Value 會設置為 'unbound
。
這段話不知道怎么講更好,貼一下原文
The system creates a symbol whenever it encounters a text reference to the symbol for the first time. When the system creates a new symbol, the value of the symbol is set to unbound.
意思就是創建一個變量會自動生成與之對應的一個 symbol ,默認值為 'unbound
,這個過程是非顯式的。
這樣,如果需要將一個變量設置為 未定義/未綁定 的狀態,則可以將它賦值為 'unbound
。
舉個例子:
-
給 arg 賦值為 12,然后打印出來。
arg = 12 println( arg ) ; 終端會打印 12
-
給 arg 再賦值為
'unbound
,然后嘗試 print 一下。arg = 'unbound println( arg ) ; 終端會提示 *Error* eval: unbound variable - arg
當一個變量沒有被定義過的時候,引用它但不賦值系統會報 Error ,但如果只是想判斷某個變量是否被定義了,可以使用函數 boundp
去檢測目標變量的 symbol name。
例如,檢測一下變量 arg 是否被定義:
-
arg 已經被賦值為
'unbound
現在是未定義的狀態。boundp( 'arg ) ; 結果是 nil
-
再給它賦值一個值。
arg = 1 boundp( 'arg ) ; 結果是 t
判斷一個函數是否存在的時候也需要利用到 symbol。
例如,判斷一下函數 dbCreateRect
是否存在:
dbCreateRect 是 Virtuoso 自帶函數。
fboundp( 'dbCreateRect )
; 結果是 lambda:dbCreateRect
判斷一下,函數 QWE123
是否存在:
fboundp( 'QWE123 )
; 不存在,結果是 nil
鏈表 list
鏈表其實不是一種數據類型,而是一種數據的存儲結構。
-
定義方式
-
list
list( arg1 arg2 list( arg3 arg4 ) ... )
listA = list( 1 2 ) ; ( 1 2 )
-
'
'( "value1" sym1 (1 2) ... )
這種表達方式需要注意,它不適用於變量元素,例如上面
sym1
並不會帶入變量值,而是 symbol 類型'sym1
listB = '( 3 4 ) ; ( 3 4 )
-
:
arg1 : arg2
僅在只有兩個元素時使用,通常用來表達一個坐標點
point = 1 : 2 ; ( 1 2 )
-
-
相關函數
-
append 連接兩個 list
listC = append( listA listB ) println( listC ) ; ( 1 2 3 4 )
-
append1 往末尾追加元素
listD = append1( listC 5 ) println( listD ) ; ( 1 2 3 4 5 )
-
cons 往開頭追加
listE = cons( 0 listD ) println( listE ) ; ( 0 1 2 3 4 5 )
-
reverse 翻轉一個 list
listF = reverse( listE ) println( listF ) ; ( 5 4 3 2 1 0 )
-
length 獲取一個 list 元素個數
length( listF ) ; 6
-
car 提取第一個元素
car( listF ) ; 5
-
cdr 提取除了第一個元素之后的 list
cdr( listF ) ; ( 4 3 2 1 0 )
-
cadr 及其更多組合
cadr( listF )
其實就是car(cdr( listF ))
的簡寫cadr( listF ) ; 4 caddr( listF ) ; 3
-
nth 根據索引提取
nth( 0 listF ) ; 5 nth( 1 listF ) ; 4 nth( length(listF)-1 listF ) ; 0
-
數組 / 向量
數組 和 向量 不常用,了解一下長什么樣就行。
數組(不常用)
-
定義
declare
declare( ARRAY[10] )
-
賦值
ARRAY[2] = 4 ARRAY[3] = 5
-
引用
println( ARRAY[2] * ARRAY[3] ) ; 20 println( ARRAY[0] ) ; unbound println( ARRAY[11] ) ; *Error* arrayref: array index out of bounds - ARRAY[11]
向量(不常用)
-
定義
makeVector
VECTOR = makeVector( 10 )
-
賦值
VECTOR[2] = 4 VECTOR[3] = 5
-
引用
println( VECTOR[2] * VECTOR[3] ) ; 20 println( VECTOR[0] ) ; unbound println( VECTOR[11] ) ; *Error* arrayref: array index out of bounds - VECTOR[11]
對照表 / 哈希
某些情況下可以使用哈希來優化代碼邏輯、運行速度等等。
-
定義
makeTable
HASH = makeTable( 'HASH )
-
賦值
HASH[1] = "ONE" HASH["2"] = 2 HASH[cvId] = "cellName"
-
引用
-
查看一個哈希的所有 key / value
HASH~>? ; ( "2" 1 db:0x21cfda1a ) HASH~>?? ; ( "2" 2 1 "ONE" db:0x21cfda1a "cellName" )
-
遍歷一個哈希
foreach( key HASH printf( "key: %A , value: %A\n" key HASH[key] ) ) ; key: db:0x21cfda1a , value: "cellName" ; key: 1 , value: "ONE" ; key: "2" , value: 2
-
條件判斷
if
-
真/假 情況運行的都是單一的語句
if( 條件 當條件成立時運行 當條件不成立時運行 )
例如:當 a > b 成立時,打印 "Yes";不成立時,打印時 "No"。
if( a > b println( "Yes" ) println( "No" ) )
-
真/假 情況需要運行多條語句
if( 條件 then 當條件成立時運行 1 當條件成立時運行 2 else 當條件不成立時運行 1 當條件不成立時運行 2 )
例如:當 a > b 成立時,c 賦值 1 然后打印 "Yes";不成立時,c 賦值為 0 然后打印時 "No"。
if( a > b then c = 1 println( "Yes" ) else c = 0 println( "No" ) )
-
多層嵌套
Skill 不支持
if-elsif-else
這種簡化的寫法,但是邏輯是一樣的,后級的if
需要在上一級的else
中。例如:
- 當 a > b 成立時,打印 "Yes";
- 否則當 a > c 成立時,打印 "Yes";
- 都不成立時,打印時 "No"。
if( a > b println( "Yes" ) if( a > c println( "Yes" ) println( "No" ) ) )
第二個
if
雖然不是 單一語句,但可以將整個if( a>c ... )
看做一個整體,所以也可以忽略then
/else
。這就存在一個問題,當需要判斷多個條件的時候
if
的寫法就不太好看了,這時就可以使用case
或者cond
,后面會講。
when / unless
when / unless 就非常簡單了,只當給定的條件為 真/假 的時候才運行給定的語句
條件為真才運行。
when( a > b
println( "Yes" )
)
條件為假才運行。
unless( a > b
println( "No" )
)
case / cond
前面說了 case
/cond
可以用來優化多條件下的 if
,因此邏輯是一樣的。
-
case
當所有的條件都是對一個變量做是否相等的判斷的時候,可以使用
case
。例如,現在有一個變量 arg:
- 當 arg 等於 "a" 時,打印 "Is a";
- 否則當 arg 等於 "b" 或 "c" 時,打印 "Is b or c";
- 否則當 arg 等於 "d" 時,打印 "Is d";
- 全都不成立時,打印 "Unmatch"。
case( arg ( "a" println( "Is a" ) ) (( "b" "c" ) println( "Is b or c" ) ) ( "d" println( "Is d" ) ) ( t println( "Unmatch" ) ) )
上面的語句換成
if
需要這樣寫:if( arg == "a" println( "Is a" ) if( arg == "b" || arg == "c" println( "Is b or c" ) if( arg == "d" println( "Is d" ) println( "Unmatch" ) ) ) )
很明顯
case
的寫法更加清晰,觀感上也更加舒服。 -
cond
case
的使用情景比較單一,但條件多且判斷的對象或者邏輯不唯一的時候可以cond
。例如,現在需要對變量 a 做幾個判斷:
- 當 a > b 時,打印 "Bigger than b";
- 否則當 a > c 時,打印 "Bigger than c";
- 都不成立時,打印 "Smallest"。
cond( ( a > b println( "Bigger than b" ) ) ( a > c println( "Bigger than c" ) ) ( t println( "Smallest" ) ) )
循環控制
for
指定一個起始整數(initialValue)和終止整數(finalValue),依次遍歷從 initial 到 final 組成的 list 的每一個元素,間隔為 1。
下面用 for
打印從 0 到 2:
for( x 0 2
println( x )
)
; 0
; 1
; 2
foreach
指定一個 list ,依此遍歷每一個元素。
下面用 foreach
打印從 0 到 2:
foreach( x list( 0 1 2 )
println( x )
)
; 0
; 1
; 2
while
指定一個條件,當條件為真時才會運行,當條件為假時跳出 while 循環。
下面用 while
打印從 0 到 2:
a = 0
while( a < 3
println( a )
a++
)
; 0
; 1
; 2
運算
算數運算
操作符 | 函數 | |||
---|---|---|---|---|
加 | + | a + b | plus | plus( a b ) |
減 | - | a - b | difference | difference( a b ) |
乘 | * | a * b | times | times( a b ) |
商 | / | a / b | quotient | quotient( a b ) |
余 | remainder | remainder( a b ) | ||
乘方 | ** | a ** b | expt | expt( a b ) |
開方 | sqrt | sqrt( a b ) |
賦值運算
操作符 | 函數 | |||
---|---|---|---|---|
直接賦值 | = | a = 1 | setq | setq( a 1 ) |
自增 | += | a += 1 | ||
自增(+1) | ++ | a ++ | add1 | add1( a ) |
自減 | -= | a -= 1 | ||
自減(+1) | -- | a -- | sub1 | sub1( a ) |
比較運算
操作符 | 函數 | |||
---|---|---|---|---|
相等 | == | a == b | equal | equal( a b ) |
不等 | != | a != b | nequal | nequal( a b ) |
小於 | < | a < b | lessp | lessp( a b ) |
小於等於 | <= | a <= b | leqp | equal( a b ) |
大於 | > | a > b | greaterp | greaterp( a b ) |
大於等於 | >= | a >= b | geqp | equal( a b ) |
幾乎相等 | nearlyEqual | nearlyEqual( a b ) |
邏輯運算
操作符 | 函數 | |||
---|---|---|---|---|
與 | && | a && b | and | and( a b ) |
或 | || | a || b | or | or( a b ) |
非 | ! | ! a | not | not( a ) |