歡迎關注個人公眾號摸魚范式
轉載自:https://zhuanlan.zhihu.com/fpga-hw-discussion
作者:知乎用戶
生的安格斯牛排
PERL
馬上開始
- 推薦一個online Perl editor,隨寫隨執行,我經常用來驗證吃不准的寫法(間接說明Perl的蛋疼),免費的。
- 后面所有代碼都可以直接copy+paste到online editor里面試運行,邊試邊寫才好。
https://www.jdoodle.com/execute-perl-online
然后再推薦一個中文Perl使用速查工具網站(很基本,但高級用法不全面)
www.runoob.com
Perl腳本的主要用途
- 最重要的就是處理文本,和一些Hash類的config數據;
- 因為Perl也支持類操作(簡單的繼承,貌似沒有虛函數功能-就是類函數的重定義),因此,一些in-house的EDA flow會用Perl作為接口語言。比如nv的Clock design system.
- 鋪墊了很多Perl如何語法不嚴謹,但是,看完這篇文章,你會覺得Perl也就那么回事。注意,本文並沒有提供特高級的Perl套路,僅僅用於普通IC工程師的普通操作。
套路1:變量
- Perl語言其實沒有數據類型的區別,任何數據(整數、浮點數、字符、字符串)都一視同仁,隨便處理。
- Perl根據數據的組織形式,分為標量($)、數組(@)、哈希量(%)
- 變量有作用域的區別,一般my表示局部變量(怎么局部,以code block決定,或者不管它,無腦my->my過一次就不能再my了。。);our表示全局標量。
# 普通變量(標量)
my $var = 1;
$var = 'abc';
# 不能再my啦!雙引號允許使用轉義字符,單引號不行
# 比如'\n',就是認為是\n,而不是換行符
# 數組
my @arr = ('a', 1, 'string a b');
# 哈希
# 很少使用%my_hash這樣的形式定義hash, 這個叫顯性定義
# 更多的是使用所謂“隱性”定義,例如
$my_hash = {
'chip_name' => 'ga100',
'tree_path' => {
'current' => '/home/abc',
'old' => '/home/xyz',
},
}; # Perl語言對於白空格很隨意,
# 用分號結尾即可
print $my_hash->{'chip_name'} , "\n";
(結果是) ga100
print $my_hash->{'tree_path'}->{'old'}, "\n";
(結果是) /home/xyz
套路1.1:特殊功能和變量
$_
$_ # 可以將它理解為,
#當前代碼塊的默認變量
# 尤其常用於foreach
# example
my @arr = (a, b, c);
# 定義數組,字符可以忽略引號
print $_, "\n" foreach @arr;
# 這里$_ foreach循環中的當前元素
# 輸出
a
b
c
@_
@_ # 用於函數傳參,
# 對!Perl的子函數,沒有C那樣的參數列表,
# 想傳什么就傳什么
# 具體例子在函數套路中講解
die / unless
die # 其實就是強制異常退出
# 比如
die ("Error happened!");
# 這句話強制讓腳本退出,
# 且在terminal輸出這句話,作為error msg.
# ===========
# 配合unless. unless其實的作用類似if語句
# 比如
die ("File $file_path is not found.") unless (-e $file_path);
# -e 是代表檢測文件是否存在
# 翻譯過來就是,強制退出並提示信息,除非!$file_path文件存在.
next/last : 用於循環中,顧名思義,next強制跳過當前循環,進入下一個。last表示,強制結束本次循環,跳出循環block(不管循環還有多少次結束)
chomp : 非常常用!用於去掉字符串結尾的換行符,也就是\n。為啥?
my $path = `pwd`; # ``表示執行shell命令
die ("The folder not exist.") unless (-d $path);
# die/unless出現啦, -d表示檢測文件夾是否存在
# 但是!無論$path存在與否,這句話肯定會強制退出,
# 因為`pwd`的返回字符串結尾有換行符,經常在這里吃虧。
# ========================
# 換種方式
my $path = `pwd`;
chomp($path); # 去掉換行符
die ("The folder not exist.") unless (-d $path);
# 這樣就不會因為換行符的緣故,誤退出了。
套路2:邏輯分支(if..else.. foreach)
if..else.. 巨簡單。
if ($var == 1) {
# 對於數值量比較, == , <= , >= , < , > 都可以
} elsif {$var eq 'abc'} {
# 這里注意!第一,Perl使用elsif, 而不是elseif !
# 注意這里用的是eq !
# 對於字符串的相等,只能用eq (equal) 和 ne (not equal)。
} else {
# 嗯,else還是和其他語言類似的
}
foreach循環
# foreach循環其實有兩種套路
# 普通的
my @arr = (a , b , c);
foreach my $elem (@arr) {
# 注意:my的使用,因為$elem前面沒定義,
# 所以這里必須定義用my。
# 其次, elem的作用域僅僅在這個foreach block
# 括號()是標准格式哦,常常忘記
print "Current elem is $elem \n"; # 常規用法
# 為啥不能用$_ ?因為你都指定每個元素放在$elem里啦!
}
# 想用$_ ?
foreach (@arr) {
print "Current elem is $_ \n" ;
# 可以的。沒必要為了用$_而用,看個人習慣。
}
===
# foreach循環最常用的是搭配哈希,
# 為啥,因為IC環境里面,hash存儲着大量的config數據,
# 需要拿出來各種操作
my $my_hash = {
'ga100' => 'go_fab',
'gv100' => 'shipped',
'gm100' => 'shipped'
};
foreach my $chip_name (keys %{$my_hash}) {
# 這里有點復雜:
# keys 是Perl內建命令,用於返回參數hash當前層次的所有key name
# %{} 表示將隱性定義的hash強制轉換為顯性,
# 為啥,因為keys只認顯性hash
# $my_hash 你會迷糊,$不是表示標量么?
# 對,但這里的標量可以理解為hash數據結構的頭指針
print "$chip_name status: $my_hash->{$chip_name} \n" ;
}
(結果是)
gm100 status: shipped
ga100 status: go_fab
gv100 status: shipped
正則表達式 (regular expression -> regex)
- 什么是正則表達式?(這個定義問題真的難到我了,以下來自百度)
正則表達式通常被用來檢索、替換那些符合某個模式(規則)的文本。
-
正則表達式能用到哪些地方?
-
- Perl/Tcl/etc (用於腳本中,對string進行搜索、替換,其表達式語法是通用的,並不因為腳本不同有區別)
- sed/grep (Linux shell cmd)
- vim/gvim (用於完成文本查找、替換)
-
為什么要提到正則表達式?
-
- 因為Perl只所以被廣泛使用,就是因為內建強大的正則表達式功能,配合靈活的語法,輕松完成腳本內文本匹配、搜索、替換等功能。
正則表達式 (regex)難不難?
對於剛剛接觸它的同學來說,略不友好,其語法、各種各樣的靈活組合,看起來有點像天書。如下舉例:
(?!(.*_PADCAL_MASK|.*HBM_MISR_MASK|.*POWER_CTL_MASK|.*RG_CTL_MASK))(.*MASK.*)
有點懵逼,是吧。淡定,我選擇了一個復雜度較高的例子。大部分日常使用的正則表達式並沒有這么復雜。
強烈推薦下面這個免費的regex tester:它提供了online的regex測試、解析,並且例舉了全部的可用語法元素。當我碰到不確定的正則表達式 (regex),我就會來這里去驗證。
下面開始舉例+學習 (for Perl)
再次說明,只看不試是不行的,把下面的regex表達式copy到上面的Online tester里面試試。
既然regex是用來做文本操作,那么我們先設置一個sample text
# 在Perl中定義一個這樣數組,4個元素
my @path_arr = (
'/home/scratch.john_gpu/gv100',
'/home/scratch.mike_gpu/ga100',
'/home/scratch.mike_gpu/lr10',
'/home/scratch.ema_ate/regression'
);
問題1: 找到所有含_gpu的元素
foreach my $elem (@path_arr) {
# foreach大家還記得吧
if ($elem =~ /_gpu/ ) {
print "$elem \n";
}
}
#結果是:
/home/scratch.john_gpu/gv100
/home/scratch.mike_gpu/ga100
/home/scratch.mike_gpu/lr10
-
這個符號組合 =~ 是什么?
-
- =~ 是Perl語言中使用正則表達式去判定“是否命中”
- 同理,還有一個 !~ ,表示用Regex判定“是否沒命中”
-
/_gpu/ 是啥?
-
- / / 是正則表達式的邊界符,里面的稱之為“正則表達式”
- _gpu 就是一個最簡單的正則表達式,因為我想搜索哪個元素帶有_gpu,那就直接用明文啦。
問題2:找到所有含_gpu/ga100元素
foreach my $elem (@path_arr) {
if ($elem =~ /_gpu\/ga100/ ) {
print "$elem \n";
}
}
#結果是:
/home/scratch.mike_gpu/ga100
-
正則表達式是啥? _gpu\/ga100
-
為毛不直接用 _gpu/ga100 ?
-
- 因為你想搜索的符號/與regex的邊界符沖突,對於這種情況(即被搜索符號與regex的語法元素沖突),使用反斜杠 (back-slash)進行轉義(escape-char)。類似換行符\n
問題3:找到符合mike**ga100的元素 (*表示不在乎中間是什么字符)
foreach my $elem (@path_arr) {
if ($elem =~ /mike.*ga100/ ) {
print "$elem \n";
}
}
#結果是:
/home/scratch.mike_gpu/ga100
-
這是什么-> .*
-
-
這是一個基礎的正則表達式啦,要分開說:
-
. 表示任意單個字符 (無論是數字、字母、標點符號、任意的東西)
-
* 表示約束預期出現的個數,允許0-無窮
-
- 有沒有人想問,1-無窮用哪個? + 咯
-
.* 表示,匹配任意字符且出現任意個數。匹配任意東西。
-
-
也許,到此為止,對 .* 還是不理解,那么:
-
- /w* 首先,/w表示任意字母,還是表示任意個數。那么,/w 表示預期匹配任意個數的字母。
- /d* /d表示0-9的任意數字,/d*就是預期匹配任意個數的數字啦。
- /d+ /d表示0-9的任意數字,/d+就是預期匹配至少出現一次的數字啦。
問題4:(變得稍微復雜啦) 找到所有g?100的元素(?定義為任意一個字母),並且將g?100這部分字段提取,並打印出來
foreach my $elem (@path_arr) {
if ($elem =~ /(g\w100)/ ) {
print "$1 \n";
}
}
#結果是:
ga100
gv100
-
\w是啥,還記得吧,表示任意字母。
-
為啥沒*了
-
- 因為題目要求只要一個文字呀
-
這次怎么多了一個括號?
-
- 這是關鍵點,這是Regex的匹配且提取
- 提取什么?能匹配括號里regex的字段咯。
-
$1是什么?
-
- 這是Perl語法,如果if語句中,能夠發生匹配成功,則$1, $2, $3 ... 存儲了每個括號中的匹配字段。
- 能有$2么?能啊,如果有兩個括號,且都匹配成功
問題5:(我們要嘗試替換啦) 找到所有g?100的元素,並且將g?100替換成lr10
foreach my $elem (@path_arr) {
$elem =~ s/g\w100/lr10/ ;
print "$elem \n" ;
}
#結果是:
/home/scratch.john_gpu/lr10
/home/scratch.mike_gpu/lr10
/home/scratch.mike_gpu/lr10 # 這個出現意外不
/home/scratch.ema_ate/regression # 這個出現意外不
-
意外吧,為啥后面兩個也出來了?
-
-
因為 $elem =~ s/g\w100/lr10/ ; 這句話,只是匹配然后替換;如果沒有匹配成功,自然不會發生替換。
-
s/// 是啥。
-
- 這個是標准的regex替換表達式,s表示替換操作,/// 是分隔符,分隔出了兩段空間。
- 第一段空間是匹配表達式
- 第二段空間的意義: 如果匹配成功,則將符合匹配的字符串替換成第二段空間的文字。
-
問題6:(再高級一點)找到所有g?100元素,並將原字段替換成大寫的。
foreach my $elem (@path_arr) {
if ($elem =~ /g\w100/) {
$elem =~ s/(g\w100)/\U$1\E/ ;
print $elem, "\n" ;
}
}
#結果是:
/home/scratch.john_gpu/GV100
/home/scratch.mike_gpu/GA100
-
print函數沒用錯,可以用逗號進行字符串拼接
-
s/// 出現啦:
-
- 第一段中,為啥有括號,因為我們不光要匹配,還要抽取匹配的字段
- 第二段,$1好理解吧,\U\E是一個搭配組合,表示中間的字段轉換為大寫。
問題7:Perl常見的傳參變量內容替換套路
這樣的代碼很常見:傳遞過來一個變量A,我想對A中的字符串內容進行一些操作(比如替換),但我又不想更改A的內容,那么:
my $A = 'my name is jason' ;
(my $B = $A) =~ s/jason/emma/;
print "A = $A \n";
print "B = $B \n";
#結果是:
A = my name is jason
B = my name is emma
寫在最后
本篇內容重在拋磚引玉(cover的內容其實很淺),我牆裂建議學習regex的時候,將更多的嘗試和驗證放在online regex tester上,實踐出真知嘛。