pretty code

顯示具有 yosys 標籤的文章。 顯示所有文章
顯示具有 yosys 標籤的文章。 顯示所有文章

2025年6月14日 星期六

期待的功能終於有人實做了

之前一直覺得是不是哪邊文件沒看清楚?不然沒道理 Yosys 合成出來的都是 escaped identifier 的名稱,這樣看起來確實是不好看!

剛剛本想直接 trace code,想說還是先用關鍵字在 issues 查一下,果然目前是沒有 rename 這樣的機制。

幸好,有一位強者提交了這個功能,等到通過後,我就可以拿 Yosys 綜合 ORFS 裡的一些專案得到一個相對大型的 netlist 來用,不然想要開發或測試一些功能都不方便。

目前還是只能看到醜醜的 escaped identifier!

看到這樣的結構,想開發一些功能都沒勁了XD

2024年7月10日 星期三

2024 week 28 新玩意

01. 工作站 grep

假設我要找 cell,但不要 .db 的檔案。

grep -r pattern --exclude="*.db"

也可以正向表列

grep -r pattern --include="*.lib"

至於為什麼要加上雙引號?那是因為整個指令傳給 grep 時,*.lib 會被 shell 轉譯過一遍,所以會造成問題,因此要加上雙引號或單引號。

不過照連結所述,當前目錄如果沒有 .lib 檔案應該是要沒問題的,我不確定是否是工作站 grep 版本過舊導致。


晚上用 Windows 11 WSL - Ubuntu 22.04.2 LTS 試了一下,確實是不用加上星號,看來是版本問題?


2024/07/12 更新

更精確來說,既然問題是出在被 shell 過了一手,那應該是 shell 版本的問題,工作站使用的是 tcsh,在我改用 bash 後,這個問題就消失了。

為了之後工作效率著想,以後不管是 glob or regex pattern,最好都用雙引號框住,才不會在急著需要做甚麼事的時候又出問題。

晚上在 WSL 裝了 tcsh 6.21.00,不加上雙引號仍然會有問題,開始懷疑是 tcsh 的 feature 了XD



02. Yosy 執行方式

Yosys 總共有好幾種執行方式:

Run directly
直接執行,手動下 command。

-c *.tcl ( refer to tcl command )
將指令寫在 tcl script 中,最前面要加上 yosys -import,才可以執行 yosys command。

使用這個模式的好處是可以用 tcl 語法,靈活性最大。

-s *.ys ( refer to script command )
將指令寫在 script 中,與 -c 差別在不需要 yosys -import 就可以執行 yosys command。

也因為不是走 tcl callbak,故整個檔案的解析都在 run_frontend 這個函數中。

yosys *.ys
同上,會在 run_frontend 中,執行 default auto command 並靠副檔名決定執行 script command。

嚴格來說上面兩個 run_frontend 執行的地方不一樣,這裡是在 while (optind < argc) 的敘述裡。

yosys xxx
因為沒有副檔名可供判斷,故在 run_frontend 中便會報錯。


03. 工作站 Shell

因為 EDA tool 緣故,大部分工作站預設的 Shell 都是跑 csh(tcsh)。但是自己寫的 Shell script,如果第一行沒有指定 Shell,則預設是跑 bash。

我想一般人應該都是比較熟悉 bash 吧?我不確定此行為是我們工作站獨有,還是 Linux 都是這樣?不過經我剛才在 WSL Ubuntu 上 22.04 實測,看起來未指定第一行會以預設 Shell 來執行。


04. alias 進階用法


雖然我後來才知道有 realpath 這個外部 command 可用,但當初想知道如何用 awk 達成此目的,也算在 shell script 中撞了一堆坑,故想要留下個記錄。以後凡事還是要先 Google,畢竟你的需求別人應該也早就碰到過了,很多事應該不用都自己來才合理。

話說回來,想要用 alias 包裝 command,遇到像雙引號或是 $ 這種特殊符號,就是用接續的方式來處理指令。

畫橘色線的是一個一個串接指令的字串,雙引號和 $ 是直接在 Shell 中,故要加上跳脫字元。

我這裡用的 shell 是 bash,tcsh 也是同樣的道理,差別在 alias 語法不是用等號而是空白。

2024/07/19 更新

晚上實測一下,realpath 無法接受 pipeline,故也不是萬能!

想要在當前目錄下找 Cell Library - realpath *.lib
想要用 grep 遞迴搜尋 Cell name - 還是只能用 awk 也就是這邊用的 alias ($9 改 $1 比較通用,有需要再用 awk 切一次)方式。

2024年7月3日 星期三

我果然是 C++ 白癡

想看的 EDA 文章都看得差不多了,今天下午想說來試試將 Python netlist parser porting 到 yosys command。

大概花了 5 個小時,才只完成 80 %,Python code 其實只有 701 行而已。

目前只剩下 save_netlist 相關 code 待移植以及修改 std::getline 呼叫方式。

不想用 namespace std 就必須多打好多 code(std::xxx),有些東西 Standard C++ 也沒有。

至少 regex, read_file line by line 有現成的可用,不過貌似 gcc std::regex constructor 很慢?待確認中。


2024/07/04 更新

早上改完 std::getline 行為後,原本以為可以順風順水,沒想到搞到 12 點多還是有一個莫名的 Segmentation Fault 問題。

下午動用了 GDB,還是看不出是死在自己程式碼的哪個地方,只能看到呼叫一堆的 std::regex 函數?

使用萬能 printf 大法,終於找到出問題的地方。搞到快 4 點多,突然想要 Google 看看,才發現居然是 std::regex 自己的問題,詳此處。 

簡單來說,要做 std::regex_search 的字串不能太長,不然 regex 因為使用遞迴實作,會有 stack 爆掉的問題,難怪 bt 指令看到的都是 regex 函數,Bug 回報的是 100 K 左右就會有問題了,最好的解決方式是改用 boost::regex。

而我手上的 netlist 居然會有 1.5 M 長度的 wire 宣告字串,還不只一條,難怪會死得一塌糊塗!

為了這個不是自己的問題,save_netlist 的功能都還沒 porting,只好之後再說了。 

順便記錄一下 C++ 與 Python 版的差異,為了快速驗證,直接將 netlist 檔案裡 3 條過長的 wire 宣告拿掉。

C++  - 106 seconds, 4G RAM.
Python - 255 seconds, 10G RAM, 離開 Python 約需 7 秒才能回到 shell 提示字元。

因為偷懶,我的 C++ 版本都還沒優化,果然 C++ 還是有其效能上的優勢,不過只是照本宣科 porting 所花的時間都快到 Python 開發時間的一半了。

2024/07/08 更新

早上花了快一個小時將 save_netlist 沒 porting 完的補完,實測結果 yosys C++ 大概也要花個 22 秒左右。

C++ 版本在 save_netlist 函數裡是直接寫檔,除了 instance 的是回傳字串再寫。

Python 版本則是得到一整個 design 字串再一口氣寫檔。

不確定 Python 處理好幾百 MB 的字串效率如何?這應該是一個可以再優化的方向(60 seconds vs 22 seconds)!

下午試了一下,事情跟我想的不一樣,Python 如果只是幾百 MB 的串接字串,一口氣寫檔似乎比一個字串寫一次還來的快,單獨寫的小程式測試結果也是如此?不確定是跟 list 中 iteration 有關還是跟寫檔 buffer I/O 有關?

另外,我測試 yosys command 時繼承的是 Backend,exexute 傳進來的 filename 只有空字串,還需要呼叫 extra_args 才會將檔名從 args 抓出來寫到 filename。

我也順便改了 code,直接用 std::string function 取代 wire regex,也算是避開了 std::regex bug 了。

我是不是全台灣最懂 yosys 的人了XD

2024年7月2日 星期二

一週雜事記錄

01. 夜翼面紗終於到手

應該是聽小老婆演唱會 03/02 那日子附近想要打到這顆頭盔,終於在 06/30 出貨了,不像喬丹之石可以用數學機率解決,這顆頭盔的掉落只能訴求天意,這意外之喜的掉落也算是給這苦悶的日子一點小小慰藉。

體感上似乎殺傷力變強了?現在只差讓這支冰法升到 96 級便算完美。

02. Pixel 8a 入手

終於買了一支超過 6500 的手機了,兩年前那支 Sharp 本想用到明年再換,但最近手機的體感溫度變強,加上上星期五因為過熱沒有網路導致一連串的鳥事發生沒有吃到中餐,晚上回到家後已然身心俱疲,於是便直接購買了這支手機,反正四年前就想買 pixel 4a 了,現在買也還不晚!

昨天到手後便開始轉移資料,不過一開始便不順利,不確定是哪邊沒操作好,後來只好一個一個 App 慢慢裝回來,還好我需要安裝的軟體不會超過 10 個。

台北等公車兩年前還可以用安裝舊版軟體的方式解決匯入常用站牌的問題,昨天雖然能安裝,但一執行便要求更新軟體,故沒有機會執行匯入功能,現在想想應該是 Android 14 或作者的機制導致?

兩年前隨便打的帳號資料還是一登入就同步下來蓋過本機資料,不意外的一樣沒有提供付費版功能,這個程式設計師一點都不值得尊重。

為了偵測手機溫度確認問題,上星期五晚上隨便下載了一個 App 試用,玩了一下馬上花了第二最高金額 300 贊助,這才是值得尊重的程式設計師,不會為了硬要賺錢死不提供付費版及拿掉必要功能。

無奈最近百廢待興,只好重新註冊一個 yahoo 帳號來同步資料,反正現在只有每個月剪頭髮回家那趟會用到,下次換手機應該不會再裝它了。

台北等公車不裝還無所謂,Daily Money 不能使用就比較麻煩,原來 Android 14 開始,Target SDK 至少要大於等於 Android 6.0 (23),故我前幾個月 build 的 Apk 已無法使用,幸好還可以使用 adb 方式安裝,xxx.apk 指的是 PC 路徑,不是 Android 路徑。

allow usb debug function
adb device
adb install --bypass-low-target-sdk-block xxx.apk

由於之前改 source code 是 hardcode 寫死 db 路徑,故要先建立 /sdcard/bwDailyMoney 資料夾再去啟動 Daily Money。

Detail Layout 使用 To - Money 才是我習慣的顯示方式。

目前記帳資料筆數來到 21,163 筆(2024/01/13 - 20,450)。

剛試了一下,程式也沒重新編譯,原本不能用的資料備份到 Google Drive 功能現在也可以使用了,不確定為什麼就是了?

目前只剩 Garmin 手錶收不到通知待解。

03. 管委會交接

昨天交接完後,終於可以無事一身輕了,可惜的是還是沒有完成用 Vue.js 開發管委會報表的 Web App,一開始應該用 pure javascript 就好了,想想還是覺得遺憾!

既然已經花了不少時間建立這 10 年的電子檔資料,完美就差臨門一腳,無奈…

04. Add command in yosys

試了一下,果然跟我想的一樣,找想要的 Pass 繼承即可,也順便寫了簡報分享給同事!

今天寫完簡報才發現原來 yosys 網頁已經提到了,不過我的簡報還是解釋得比較清楚就是了XD

另外這篇文章也寫得不錯,裡面有提到 netlist 其實沒有標準的問題,但照 yosys 所述,assign signal 應該需要被支援才對,改天再加到我的 netlist parser,左右兩邊都把它當 wire or pin arg 解析應該就可以了?

2024年6月12日 星期三

關於 Verilog parser 這件事

連假都在盡最後努力讓自己的聖騎升上 97 級,再加上手上沒有那樣大的 netlist 檔案,所以放假三天就在打電動中渡過了XD

昨天一早就用 cProfile 確定最慢的執行函數是什麼?果不其然,前三名是 PLY yacc.py - parseopt_notrack、 PLY lex.py - token 以及 re.match,第四名 pyverilog parser.py - p_items 看起來貌似 constructor 沒錯?


光前三個就跑了 25 分鐘以上(cProfile 會增加程式執行時間,跑起來要更有耐心)。

要細究是哪個段落花時間只能 print message 了,但我想幫助不大,因為 PLY 應該已經是 Python 最有名的 lex/yacc solution 了,要優化應該也不是一天兩天的事。

順便實測其他 EDA tool 的花費時間,原來也不是我想的秒等級!

雖然這樣比有點不公平,比如 DC 還需要知道 cell library,還有不論是 DC or yosys,貌似 read_verilog 不只是 parsing 都還順便建立了 RTLIL 中介層,應該是比 pyverilog 只用一般資料結構儲存花時間,但也只能這樣比較了,畢竟我已經無法再細拆 command 了。

同樣的 netlist 檔案,花費秒數如下:

DC - 191
yosys - 380
pyverilog (建完語法樹) - 1546,離開程式 Python 釋放記憶體又花了至少 100 秒。

嗯,果然還是有一段差距。

2024/06/18 更新

透過 cProfile 分析得知,parser 花了很多時間做 match,撇開 C vs Python 語言差異不談,最快的解決方式就是只要處理 netlist 語法即可,不需要處理整個 Verilog 語法。

稍微查了一下,只有看到 C 語言版本的 netlist parser,再加上看 EDA 文件真的很 borning(EDA 文件都要真的操作才有感覺,還沒碰過的光看文件,坦白說收益不大),所以還是自己來吧。

從上星期五開始,扣掉家裡有事請假外,總共花了 16.5 小時使用 Python 實作。

概念也是非常簡單,我有一個函數負責取得以分號為結尾的字串或是 endmodule 關鍵字,這裡我稱呼它為工作字串,再來就是對工作字串做比對,只要分別針對 module,port,net,cell instance 處理並儲存結果即可。

目前暫時還沒想到 assign 要儲存啥以及偷懶不想處理一維陣列以上的情況,其餘我知道的語法都已處理完畢。

開發時間分析如下:

得到可以工作的字串 - 2.5 小時
使用 Rex 處理所有語法 - 8 小時
將儲存的資料結構轉成 pyverilog 的輸出格式 - 6 小時

程式碼行數:

得到工作字串 - 89 行
輸出 Verilog - 149 行
Rex 處理及其他 - 455 行

程式執行時間:

單純 parsing 及儲存 - 225 秒
輸出 Verilog - 56 秒

下面則是我的 netlist parser cProfile 結果:


目前還算滿意XD

雖然從結果來看,還是有進一步優化的可能性,但我覺得最多不可能再減少超過 50 秒?

就算再減少 50 秒也沒意義!我只有處理 netlist,不像 EDA Tool 處理整個 Verilog 語法,這樣的 parser 跟商用軟體比還是有段距離,但貌似我的工作大部分應該還是針對 netlist 做事?

個人還是覺得 lex/yacc 的方法比較正統,不像我的 code 為了解決縮排,使用者 Code 亂以及空白字元等問題,需要一些 if/else 來處理(取工作字串 89 行程式碼中,大部分都是在處理這些事情),這樣的 code 看起來也不開心XD

2024/06/21 更新

前天進公司後,想了一下還是加上多維陣列的功能,大概花了不到 5 分鐘吧。

沒想到這兩天拿它來試試其他手上有的 netlist,陸續又發現了一些問題,也順便修了 pin net 中,有 range 及 array 情況下,Rex 沒下好導致 capture group 抓取不正確的問題,昨天下午看了 Verilog 2001 Spec 後還發現我搞錯順序了,應該是先陣列才是 range,目前應該都沒問題了吧?

為了這個多維陣列,code 多了 30 幾行,程式執行時間也多了 20 幾秒,目前 parsing 大概要跑 245 秒左右。

單純 parsing


單純 parsing 加上處理多維陣列結果


最後想嘗試一下把我這個 parser 用到之前拔電路的小程式中,一般來說,我有兩個選擇:

1. 將我目前用的簡單資料結構轉成 pyverilog ast 結構再加上個別 class visit function,這樣我不用去改原本的 code。

2. 配合我 parser 用的簡單資料結構改寫我原本的 code。

目前是嘗試使用方法二,今天應該就能知道結果了。

對了,這個 600 多萬行的 Verilog,我的 parser 執行時會用掉 10G 多一點的記憶體,離開好像也是要花個幾秒等記憶體釋放。

跟之前用 pyverilog 的小程式比較,大概快了 35 分鐘,也不枉費我這幾天的努力XD


我的程式總共建了 9 條 Rex。

因為我的程式把行打散了,故我無法得知行數資訊,先前簡單用個 group 概念好讓自己的輸出接近 pyverilog 格式。

之前是用 yosys read_verilog + write_verilog 方式來驗證我的 parser 正確性,我們可以用 design -reset command 將兩個要比較的檔案寫在同一個 script 讓 yosys 幫忙轉檔,同一個檔案,讀加寫要花 30 分鐘(沒記錯的話,寫檔就花了 20 幾分鐘)。

由於之前就知道要怎麼改,故最後還是把 module.port_list 再包一層,裡面儲存另一個 list,這樣我就能讓同 group 的在同一行輸出,加上這個功能之後,我的輸出基本上就跟 pyverilog 一致,除了多維陣列還未驗證過 pyverilog 格式長怎樣。

也因為這樣輸出 verilog 函數行數再度減少,最終版本 parser 包含測試 main 函數只有 701 行(先前的行數統計也是包含 main)。

目前只剩一個謎題待解?

我能理解 pyverilog 因為比對緣故所以花了很多時間,但為什麼單純輸出 verilog 也要花上 10 來分鐘,對比我的 60 秒確實是有不小的差距!我猜 pyverilog 應該是使用 template file 方式產生 verilog,故浪費了很多時間在 File IO 。

其實 yosys 寫檔時間比 pyverilog 還更誇張,我能想到的就是它又二次轉了資料結構以利 verilog 輸出?

剛剛看了一下,輸出是在 yosys/backends/verilog/verilog_backend.cc 這支檔案,看起來是用 RTLIL::Design *design 來儲存整個 netlist,並直接用 std::ostream *&f 寫到 output,看來想知道事情緣由還是要把整個程式碼看過一遍才行。

2024/06/23 更新

快速的看了一下 yosys code,backend - write_verilog 呼叫順序為:

yosys/kernel/driver.cc - main 函數入口
中間未看…
yosys/kernel/register.cc - Backend::execute(這個應該沒有,因為被 verilog_backend 繼承了)
yosys/backends/verilog/verilog_backend.cc - execute,這裡會呼叫 Backends::extra_args,裡面會把 
原本是 NULL 的 std::ostream 在這裡開檔,之後便能寫檔了,這也是為什麼 execute 會接受一個 std::ostream *&f 的緣故,因為需要在這裡 new instance。

2024/06/25 更新

yosys 可以吃 tcl script file,故有用到 Tcl 提供的介面,script 裡面要執行的 command 其 callback 在 kernel/yosys.cc - tcl_yosys_cmd。

在 Pass::call 的過程中,便會呼叫到繼承 Pass 的相關 struct。

2024/06/26 更新

沒用 C++ 寫過大型程式,別說你懂 C++!

從以前到現在沒有認真記過 C++ 語法,導致看 yosys code 很吃力!很多執行順序不是現在的我看的出來的!

The virtual function of base class means inheritance class can override it, you could use override to tell compiler the function is overrided.

baes - virtual void function( )
inheritance - void function( ) override {}

The virtual function of base class equals 0 means the base class can't be created. The inheritance class must override this function.

base - virtual void function ( ) = 0
inheritance - void function ( ) override {}

另外,如果 class 有 static function,其 各個 Pass constructor 會在 main 之前就被呼叫,可能要看 C++11 3.6 節,這是我用 gdb 單步執行才發現的(我的理解可能有誤,至少我另外寫支小程式看不到這個現象?)。

kernel/register.cc - struct Pass 有一個成員 next_queued_pass,在 constructor 時會被設定,然後透過 register.cc global variable - first_queued_pass 記住最後一個 constructor 的 Pass。

接著 main 函數中會呼叫 yosys_setup,裡面會再呼叫 Pass::init_register(執行順序會倒著回來,之前的 first_queued_pass 記住的是最後一個 Pass constructor,沒記錯的話是 techlibs/sf2 - SynthSf2Pass)。

Pass::init_register 裡面會再呼叫 Pass::run_gister。

然後就是對 global variable - pass_register 設值,map 用的 key 就是 pass_name。

之後假設是執行 script 裡面的 command,最後就是在 Pass::call 裡面利用 pass_register 去執行該指令(pre_execute、execute、post_execute),這樣又會回到繼承 Pass 的 struct 裡的 execute 函數(backends/verilog/verilog_backend.cc - execute),因為已經被 override 了。


看到這邊對 yosys 怎麼呼叫有感覺了,應該不會再看下去了?

另外,今天針對同一個 netlist 呼叫 yosys read_verilog and write_verilog,真的要花 31 分鐘左右,這樣看來寫檔真的要 20 幾分鐘跑不掉。

接著把 yosys 儲存的 netlist 再用我的 parser 測試,parsing 時間又增加了 20 幾秒,寫 netlist 還是差不多 60 秒左右,總花費時間大概是在 360 秒內吧?

pyverilog 總執行時間則是暴增到 54 多分鐘(3275 seconds)。

其實這也很容易解釋,yosys write_verilog 對每個 wire declaration 都是單獨一行,故增加了 parsing 時間也是尚屬合理。

原本 netlsit - 340M,  618 萬行。
yosys netlist - 390M, 2166 萬行。

2024/06/27 更新

終於知道為什麼繼承 Pass 的 struct 都會在 main 之前 call constructor 了!跟 static member function沒有關係。

yosys 在每個繼承 Pass 的 struct 最後面宣告處宣告了一個 global static variable,所以才會在 main 之前 call constructor。

以工作站的 yosys 版本來說,總共有 235 Pass 被初始化了。

也就是下面圖片 Line 9 做的事。


下面是 GDB 下中斷的結果


我們可以用 grep -E -r '^\}\s*[a-zA-Z0-9]+\s*;' 找出所有這樣宣告的 Pass,這裡以 backends 為例。


2024/07/08 更新


parsing 106 秒
save netlist 22 秒

對比 Python 版

parsing 268 秒
save netlist 60 秒

搞定,收工XD