https://www.cnblogs.com/yeungchie/ Cadence SkillCadence 提供二次開(kāi)發(fā)的 SKILL 語(yǔ)言,它是一種基于通用人工智能語(yǔ)言 — LISP 的交互式高級(jí)編程語(yǔ)言。① SKILL 語(yǔ)言支持一套類(lèi)似 C 語(yǔ)言的語(yǔ)法,大大降低了初學(xué)者學(xué)習(xí)的難度,同時(shí)高水平的編程者可以選擇使用類(lèi)似 LISP 語(yǔ)言的全部功能。所以 SKILL 語(yǔ)言既可以用作最簡(jiǎn)單的工具語(yǔ)言,也可以作為開(kāi)發(fā)任何應(yīng)用的、強(qiáng)大的編程語(yǔ)言。 SKILL 可以與底層系統(tǒng)交互,也提供了訪問(wèn) Cadence 各個(gè)工具的豐富接口,用戶可以通過(guò) Skill 語(yǔ)言來(lái)訪問(wèn),并且可以開(kāi)發(fā)自己的基于 Cadence 平臺(tái)的工具。 我的環(huán)境是 Virtuoso IC618 平臺(tái) ( SKILL37.00 ) ,可能存在某些特性不適用于舊版本。
如何查看官方資料
cdsFinder
cdnshelp
Skill Version
基礎(chǔ)語(yǔ)法Hello World舉例一種 Hello World 的寫(xiě)法。
注釋
|
操作符 | 函數(shù) | 備注 | |||
加 | + | 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) | 用于整數(shù) | ||
modf | modf(a b) | 用于浮點(diǎn)數(shù) | |||
乘方 | ** | a ** b | expt | expt(a b) | |
開(kāi)方 | sqrt | sqrt(a b) |
操作符 | 函數(shù) | |||
直接賦值 | = | a = 1 | setq | setq( a 1 ) |
自增 | += | a += 1 | ||
自增 (+1) | ++ | ++a | add1 | add1( a ) |
a++ | postincrement | postincrement( a ) | ||
自減 | -= | a -= 1 | ||
自減 (-1) | -- | --a | sub1 | sub1( a ) |
a-- | postdecrement | postdecrement( a ) |
Tips ??:
++a
會(huì)將 a 加一后的值返回,返回值是 2a++
會(huì)先返回 a 的值后加一,返回值是 1add1( a )
會(huì)返回 2,但不改變 a 的值postincrement( a )
會(huì)返回 1,a 的值會(huì)加一變成 2操作符 | 函數(shù) | |||
相等 | == | a == b | equal | equal( a b ) |
不相等 | != | a == b | nequal | nequal( a b ) |
小于 | < | a < b | lessp | lessp( a b ) |
小于等于 | <= | a <= b | leqp | leqp( a b ) |
大于 | > | a > b | greaterp | greaterp( a b ) |
大于等于 | >= | a >= b | geqp | geqp( a b ) |
幾乎相等 | nearlyEqual | nearlyEqual( a b ) |
操作符 | 函數(shù) | |||
與 | && | a && b | and | and( a b ) |
或 | || | a || b | or | or( a b ) |
非 | ! | ! a | not | not( a ) |
真/假 情況運(yùn)行的都是單一的語(yǔ)句
if( 條件
當(dāng)條件成立時(shí)運(yùn)行
當(dāng)條件不成立時(shí)運(yùn)行
)
例如:當(dāng) a > b 成立時(shí),打印 "Yes";不成立時(shí),打印時(shí) "No"。
if( a > b
println( "Yes" )
println( "No" )
)
真/假 情況需要運(yùn)行多條語(yǔ)句
if( 條件
then
當(dāng)條件成立時(shí)運(yùn)行 1
當(dāng)條件成立時(shí)運(yùn)行 2
else
當(dāng)條件不成立時(shí)運(yùn)行 3
當(dāng)條件不成立時(shí)運(yùn)行 4
)
例如:當(dāng) a > b 成立時(shí),c 賦值 1 然后打印 "Yes";不成立時(shí),c 賦值為 0 然后打印時(shí) "No"。
if( a > b
then
c = 1
println( "Yes" )
else
c = 0
println( "No" )
)
多層嵌套
Skill 不能用 if-elsif-else
這種簡(jiǎn)化的寫(xiě)法,但是邏輯是一樣的,后級(jí)的 if
需要在上一級(jí)的 else
中。
if( 條件 A
當(dāng)條件 A 成立時(shí)運(yùn)行
if( 條件 B
否則 當(dāng)條件 B 成立時(shí)運(yùn)行
以上條件都不成立時(shí)運(yùn)行
)
)
if( 條件 A
then
當(dāng)條件 A 成立時(shí)運(yùn)行 1
當(dāng)條件 A 成立時(shí)運(yùn)行 2
else
if( 條件 B
then
否則 當(dāng)條件 B 成立時(shí)運(yùn)行 3
否則 當(dāng)條件 B 成立時(shí)運(yùn)行 4
else
以上條件都不成立時(shí)運(yùn)行 5
以上條件都不成立時(shí)運(yùn)行 6
)
)
例如下面想實(shí)現(xiàn):
if( a > b
println( "Yes" )
if( a > c
println( "Yes" )
println( "No" )
)
)
第二個(gè) if
雖然不是 單一語(yǔ)句,但可以將整個(gè) if( a > c ... )
看做一個(gè)整體,所以也可以忽略 then
/else
。
這還有一個(gè)問(wèn)題,當(dāng)需要判斷多個(gè)條件的時(shí)候 if
的寫(xiě)法就太不好看了,這時(shí)就可以使用 case
或者 cond
,等會(huì)講。
when / unless 就非常簡(jiǎn)單了,只當(dāng)給定的條件為 真/假 的時(shí)候才運(yùn)行給定的語(yǔ)句
條件為真才運(yùn)行。
when( a > b
println( "Yes" )
)
條件為假才運(yùn)行。
unless( a > b
println( "No" )
)
前面說(shuō)了 case
/cond
可以用來(lái)優(yōu)化多條件下的 if
,因此邏輯是一樣的。
case
當(dāng)所有的條件都是對(duì)一個(gè)變量做是否相等的判斷的時(shí)候,可以使用 case
。
例如,現(xiàn)在有一個(gè)變量 arg:
case( arg
( "a"
println( "It is a" )
)
(( "b" "c" )
println( "It is b or c" )
)
( "d"
println( "It is d" )
)
( t
println( "Unmatch" )
)
)
上面的語(yǔ)句換成 if
需要這樣寫(xiě):
if( arg == "a"
println( "It is a" )
if( arg == "b" || arg == "c"
println( "It is b or c" )
if( arg == "d"
println( "It is d" )
println( "Unmatch" )
)
)
)
很明顯 case
的寫(xiě)法更加清晰,觀感上也更加舒服。
cond
case
的使用情景比較單一,當(dāng)條件多且判斷的對(duì)象或者邏輯不唯一的時(shí)候可以 cond
。
例如,現(xiàn)在需要對(duì)變量 a 做幾個(gè)判斷:
cond(
( a > b
println( "Bigger than b" )
)
( a > c
println( "Bigger than c" )
)
( t
println( "Smallest" )
)
)
指定一個(gè)起始整數(shù)(initial)和終止整數(shù)(final),依次遍歷從 initial 到 final 組成的 list 的每一個(gè)元素,間隔為 1。
下面用 for
打印從 0 到 2:
for( x 0 2
println( x )
)
; 0
; 1
; 2
指定一個(gè) list ,依此遍歷每一個(gè)元素。
下面用 foreach
打印從 0 到 2:
foreach( x list( 0 1 2 )
println( x )
)
; 0
; 1
; 2
也可以同時(shí)遍歷多個(gè) list
foreach(( x y z ) list( 0 1 2 ) list( 3 4 5 ) list( 6 7 8 )
printf( "%d %d %d\n" x y z )
)
; 0 3 6
; 1 4 7
; 2 5 8
foreach 的返回值是第一個(gè) list,( 0 1 2 )
指定一個(gè)條件,當(dāng)條件為真時(shí)才會(huì)運(yùn)行,當(dāng)條件為假時(shí)跳出 while 循環(huán)。
下面用 while
打印從 0 到 2:
a = 0
while( a < 3
println( a )
a++
)
; 0
; 1
; 2
(函數(shù))
procedure
函數(shù)來(lái)聲明。下面舉個(gè)例子:
procedure( myAdd( a b )
a + b
); myAdd
這個(gè)子程序用來(lái)實(shí)現(xiàn)輸入變量 a
和 b
,返回 a + b
的值。
myAdd
就是子程序的名稱(chēng)。a
和 b
是定義需要兩個(gè)參數(shù),這兩個(gè)參數(shù)會(huì)作為子程序的局部變量,局部變量等會(huì)再細(xì)講。a + b
是代碼塊部分,僅有一行也作為最后一行,因此會(huì)返回兩個(gè)變量相加之后的值。下面看下如何調(diào)用這個(gè) myAdd
:
myAdd( 1 2 )
; 返回 3
參數(shù)不是必須的,也可以提供一個(gè)空列表(括號(hào)里空著不寫(xiě))作為參數(shù)的定義,意味著這個(gè)子程序不需要參數(shù)。
procedure( myAddOneAndTwo()
3
); 這個(gè)子程序直接返回 3
myAddOneAndTwo()
; 3
p
let
用來(lái)定義局部變量,定義變量的改動(dòng)不會(huì)影響到代碼塊外的同名變量。用法:
a = 5
let(( a )
a = 7
println( a )
)
println( a )
返回的結(jié)果是:
7
5
如果想給 let
中的變量賦值一個(gè)默認(rèn)值,出了在開(kāi)頭寫(xiě)一個(gè)賦值語(yǔ)句,上面的 let
還可以這樣簡(jiǎn)化:
let(( a( 7 ) )
println( a )
)
prog
相對(duì)于 let
增加了 return
和 go
函數(shù)的支持。prog
的默認(rèn)返回值是 nil
由于 prog
默認(rèn)的返回值是 nil
,因此需要一個(gè)方法能夠指定返回值是什么。而 return
就能夠?qū)崿F(xiàn)在一個(gè) prog
中的任意位置跳出,并指定返回值。
示范一下:
prog(( a )
a = 1
when( a < 5
return( t )
)
return() ; 當(dāng) return 不指定的返回值的時(shí)候等同于 return( nil )
)
上面的程序由于 a
為 1 ,小于 5,因此執(zhí)行 when 中的語(yǔ)句,跳出 prog 并返回 t 。
運(yùn)用 prog + return
可以更加靈活的控制 while
、 foreach
等循環(huán)結(jié)構(gòu)。
例如下面兩段相似的代碼中,prog
的位置不同對(duì)程序運(yùn)行的影響:
滿足條件提前跳出循環(huán)
a = 0
prog(( )
while( a <= 3
a++
when( a == 2 return())
print( a )
); while
); prog
當(dāng)前 a == 2 時(shí),跳出 prog
,由于 prog
在 while
循環(huán)外部,因此整個(gè)循環(huán)會(huì)結(jié)束。
結(jié)果打印 1
。
滿足條件直接運(yùn)行下一個(gè)循環(huán)
a = 0
while( a <= 3
prog(( )
a++
when( a == 2 return())
print( a )
); prog
); while
當(dāng)前 a == 2 時(shí),跳出 prog
,由于 prog
在 while
循環(huán)內(nèi)部,因此當(dāng)前循環(huán)結(jié)束,不執(zhí)行 print
直接進(jìn)入下一次循環(huán)。
結(jié)果打印 134
。
go
用來(lái)實(shí)現(xiàn)在一個(gè) prog
內(nèi)部,跳轉(zhuǎn)到指定的 標(biāo)簽
。
prog(( a )
a = 0
LABEL ; 打個(gè)標(biāo)簽
print( a++ )
when( a <= 3
go( LABEL ) ; 跳轉(zhuǎn)到標(biāo)簽的位置
)
); prog
上面的例子實(shí)現(xiàn)一個(gè)循環(huán),判斷 ++a
的值小于等于 3 時(shí)回到 LABEL
標(biāo)記的位置重復(fù)運(yùn)行,結(jié)果打印 0123
。
go
的使用存在一些限制,不能在多個(gè) prog
之間跳轉(zhuǎn),不能往循環(huán)內(nèi)跳轉(zhuǎn):
prog
)上面我們已經(jīng)定義了一個(gè) myAdd
函數(shù),由于執(zhí)行的過(guò)程是做加法,因此它有一個(gè)隱含的要求是:輸入的兩個(gè)變量都必須是數(shù)字,否則運(yùn)行會(huì)報(bào)錯(cuò)
can't handle
我們可以在子程序的定義中加入這個(gè)變量類(lèi)型的判斷:
procedure( myAdd( a b )
unless( numberp( a ) && numberp( b )
error("myAdd: Argument should be number.")
)
a + b
); myAdd
運(yùn)行上面的函數(shù)試試:
myAdd( "1" "2" )
; *Error* myAdd: Argument should be number.
不過(guò)我們不需要這么麻煩去直接寫(xiě)每個(gè)參數(shù)的判斷,Skill 已經(jīng)提供了更簡(jiǎn)單的方法:
procedure( myAdd( a b "nn")
a + b
); myAdd
再運(yùn)行上面的函數(shù)試試:
myAdd( "1" "2" )
; *Error* myAdd: argument #1 should be a number (type template = "nn") - "1"
可以看到僅僅是在參數(shù)定義之后追加了 "n"
就可以起到效果,第一個(gè) n 聲明第一個(gè)參數(shù)需要為數(shù)字(number 縮寫(xiě)成 n),第二個(gè) n 同理聲明第二個(gè)參數(shù)。
不過(guò)這個(gè)寫(xiě)法還能簡(jiǎn)化:... ( a b "n") ...
,像這樣只寫(xiě)一個(gè) n
就代表所有的參數(shù)都必須為數(shù)字類(lèi)型。
下面舉例一些常見(jiàn)的用于聲明數(shù)據(jù)類(lèi)型的縮寫(xiě):
縮寫(xiě) | 內(nèi)部命名 | 數(shù)據(jù)類(lèi)型 |
---|---|---|
d | dbobject | id , Cadence 數(shù)據(jù)對(duì)象 |
x | integer | 整數(shù) |
f | flonum | 浮點(diǎn)數(shù) |
n | number | 整數(shù) 或者 浮點(diǎn)數(shù) |
g | general | 通用的 , 任何數(shù)據(jù)類(lèi)型 |
l | list | 鏈表 |
p | port | I / O 句柄 |
t | string | 字符串 |
s | symbol | symbol(符號(hào)) |
S | stringSymbol | symbol 或者 字符串 |
u | function | 函數(shù)對(duì)象 , 函數(shù)名 或者 lambda 對(duì)象 |
... | ... | ... |
定義輸入?yún)?shù)時(shí)候,可以使用一些 修飾 符號(hào)來(lái)做到類(lèi)似 Getopt 的效果,常用的如下:
@rest
定義 不限數(shù)量 的輸入
@optional
定義 可有可無(wú) 的輸入,需要按順序
@key
定義 可有可無(wú) 的輸入,需要指定參數(shù)名
注意 ??:語(yǔ)法上 @optional 和 @key 不能同時(shí)使用,功能上 @rest 和 @optional 同時(shí)使用會(huì)存在矛盾。
還是用上面的子程序 myAdd
來(lái)舉例。
場(chǎng)景:現(xiàn)在這個(gè)程序,只能接受兩個(gè)參數(shù)做加法,如果輸入是 3 個(gè)或者更多怎么辦? 我不知道有多少個(gè)參數(shù)需要一次性輸入。這時(shí)候就需要用到 @rest
優(yōu)化一下 myAdd
:
procedure( myAdd( @rest args ) ; 修飾符號(hào)寫(xiě)在被修飾參數(shù)的前面
prog(( result )
result = 0
foreach( num args
printf( "myAdd: %n + %n\n" result num )
result += num
)
return( result )
)
); myAdd
運(yùn)行一下:
myAdd( 1 2 3 )
; myAdd: 0 + 1
; myAdd: 1 + 2
; myAdd: 3 + 3
; => 6
例子中只定義了一個(gè)輸入?yún)?shù),被聲明 @rest
后,args
會(huì)變成一個(gè) list 參與子程序內(nèi)部運(yùn)行,接著遍歷所有元素加起來(lái)就行了。
場(chǎng)景:
myAdd
現(xiàn)在只能做加法,如果我想自定義運(yùn)算類(lèi)型,且要求不指定運(yùn)算類(lèi)型的時(shí)候默認(rèn)做加法怎么辦?這時(shí)候就需要用到 @optional
下面的例子為了避免矛盾,就不使用 @rest
了,將 args
用一個(gè) list 來(lái)輸入。
procedure( myCalc( args @optional opt("+") ) ; 參數(shù)后面的括號(hào)內(nèi)寫(xiě)上默認(rèn)值,也可以寫(xiě)成 ( opt "+" )
prog(( result )
result = car( args )
args = cdr( args )
foreach( num args
printf( "myCalc: %n %s %n ; " result opt num )
case(opt
("+" result += num )
("-" result -= num )
("*" result *= num )
("/" result /= num )
); 按照不同的 opt 執(zhí)行不同的操作
)
return( result )
)
); myCalc
運(yùn)行一下:
myCalc( list( 1 2 3 ))
; myCalc: 1 + 2 ; myCalc: 3 + 3 ;
; => 6
myCalc( list( 1 2 3 ) "-")
; myCalc: 1 - 2 ; myCalc: -1 - 3 ;
; => -4
myCalc( list( 1 2 3 ) "*")
; myCalc: 1 * 2 ; myCalc: 2 * 3 ;
; => 6
myCalc( list( 1.0 2 3 ) "/")
; myCalc: 1.000000 / 2 ; myCalc: 0.500000 / 3 ;
; => 0.1666667
場(chǎng)景:上面 @optional 的要求是輸入的參數(shù)必須按順序,即 先
args
后opt
,我不想固定這個(gè)順序怎么辦?這時(shí)候就需要用到 @key
使用格式:
定義
procedure( function( @key key1 key2 )
...
)
不指定默認(rèn)值的時(shí)候,默認(rèn)值就是 nil
運(yùn)行:
function( ?key1 arg1 ?key2 arg2 ... )
在上面 myCalc
的基礎(chǔ)上改一下:
procedure( myCalc( @key args opt("+") )
; prog ... 過(guò)程完全一致,這里就不寫(xiě)了
); myCalc
運(yùn)行一下:
myCalc( list( 1 2 3 ) "+")
*Error* myCalc: extra arguments or keyword missing - ((1 2 3) "+")
myCalc( ?args list( 1 2 3 ) ?opt "+")
; myCalc: 1 + 2 ; myCalc: 3 + 3 ;
; => 6
myCalc( ?opt "+" ?args list( 1 2 3 ))
; myCalc: 1 + 2 ; myCalc: 3 + 3 ;
; => 6
(lambda)
匿名函數(shù),顧名思義沒(méi)有名字的函數(shù),不同于 procedure
需要指定一個(gè)函數(shù)名,lambda
不需要指定一個(gè)函數(shù)名,它會(huì)返回一個(gè) lambda 對(duì)象。
sum = lambda(( a b )
a + b
)
上面已經(jīng)定義了一個(gè)匿名函數(shù),并將 lambda 對(duì)象賦值給了 sum
變量。 接下來(lái)就可以用 funcall
函數(shù)來(lái)使用它:
funcall( sum 1 2 )
; 3
funcall
的第一個(gè)參數(shù)接收一個(gè)函數(shù)對(duì)象 sum
,后面的參數(shù)依次作為輸入。
如果待輸入的變量保存在一個(gè) list 中,也可以用 apply
函數(shù)來(lái)使用它:
apply( sum list( 1 2 ))
; 3
可以看到,apply
的第一個(gè)參數(shù)接收一個(gè)函數(shù)對(duì)象 sum
,第二個(gè)參數(shù)是一個(gè) list ,list 中的元素依次對(duì)應(yīng) sum
需要接收的參數(shù)。
這里需要注意的是,并不是
sum
需要接收一個(gè) list,而是apply
接收一個(gè) list,再把其中的元素依次作為sum
的輸入進(jìn)行傳參。sum
接收到的依然是兩個(gè)參數(shù)。
此外 funcall
、apply
不光可以接收 lambda,也可以接收一個(gè) symbol 變量,前面講到 symbol 存在一個(gè) slot 是 Function binding ,因此也可以調(diào)用非匿名的子程序。
apply( 'plus list( 1 2 ))
; 3
這里的
'plus
就作為plus
函數(shù)的引用。
mapcar
的效果其實(shí)也是循環(huán),之所以放到 《子程序》 章節(jié)來(lái)講,是因?yàn)槭褂眠@個(gè)函數(shù)需要先了解子程序是什么。
假設(shè)現(xiàn)在有一個(gè) list:
numbers1 = list( 1 3 2 4 5 7 )
現(xiàn)在要將這個(gè) list 中的每個(gè)元素都 +1
,用 foreach
可以這樣做:
numbers2 = nil
foreach( x numbers1
numbers2 = append1( numbers2 ++x )
)
println( numbers2 )
; ( 2 4 3 5 6 8 )
可以看到還是比較啰嗦的,換做 mapcar
就很方便了:
numbers3 = mapcar( 'add1 numbers1 )
; ( 2 4 3 5 6 8 )
也可以接受一個(gè) lambda
匿名函數(shù):
numbers4 = mapcar( lambda(( a ) a++ ) numbers1 )
; ( 2 4 3 5 6 8 )
另外前面講到 foreach
的返回值是第一個(gè) list,配合 mapcar
后可以將每次循環(huán)的結(jié)果作為返回值。
numbers5 = foreach( mapcar x numbers1
x++
)
; ( 2 4 3 5 6 8 )
|
來(lái)自: 玄遠(yuǎn)書(shū)志 > 《EDA》