IC入職新同學必備技能手冊②


歡迎關注個人公眾號摸魚范式

轉載自: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),我就會來這里去驗證。

https://regex101.com/

下面開始舉例+學習 (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上,實踐出真知嘛。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM