這幾天又把手上的 IP test 相關 flow 又看更深了一層,在看的過程中就覺得哪邊怪怪的,原來是 module 不用一定要建立實體?
晚上回到家,寫了一個測試來驗證,好像真的不用,難道是 System Verilog 的緣故?先記錄一下,回頭再來確認!
2025/06/05 更新
早上翻遍了 Verilog 規格書,還是看不到不用實體化的理由?
於是問了一下 Bard,他也是一直跟我說不行,直到我說我用 iverilog + vvp 是可以跑的,他才告訴我一個答案,一開始我還半信半疑,不過,有了方向就好解決,打開 -v 選項一切都無所遁形!
下面是 Bard 的回答
你觀察到的現象是正確的!
你的 tb.v 程式碼能夠被 iverilog tb.v + vvp a.out 正確執行,這主要是因為 Icarus Verilog (iverilog) 編譯器的一個特性,以及 Verilog/SystemVerilog 語言中階層式參考 (Hierarchical Reference) 的規則。
Icarus Verilog 的行為:
自動辨識頂層模組 (Top-Level Modules):
iverilog 預設會將那些沒有被其他模組實例化的模組視為頂層模組 (top-level modules)。
在你的 tb.v 檔案中,cfg 和 test 這兩個模組都沒有被其他模組實例化,所以 iverilog 會將它們都視為頂層模組。
這意味著 iverilog 會為 cfg 和 test 各自創建一個獨立的實例,位於模擬器內部的一個抽象的 "root" 級別。
即使 cfg 模組沒有被 test 模組直接實例化,但 test 模組可以透過階層式參考的方式來存取 cfg 模組內部定義的訊號。
在 iverilog 預設將 cfg 視為頂層模組的情況下,它會被分配一個預設的階層名稱,通常就是它本身的模組名稱,或是在內部樹狀結構中被編譯器視為一個根實例。
所以,當 test 模組嘗試使用 cfg.num 時,iverilog 能夠解析這個路徑,找到 cfg 這個頂層模組的實例,並進一步找到它內部的 num 訊號。
為什麼這在真實硬體設計中不常見:
雖然 iverilog 允許這樣做並能正確模擬,但在實際的 RTL (Register Transfer Level) 硬體設計中,這並不是一個好的設計習慣,通常會避免。
缺乏明確的連接性:
在硬體中,你必須明確指定一個模組如何與另一個模組互動(透過實例化和端口連接)。你目前的寫法模糊了這種關係,讓人難以理解訊號是如何傳遞的。
模擬器/合成器行為差異:
雖然 iverilog 支援這種階層式參考,但其他模擬器或硬體合成器 (Synthesizer) 可能對這種寫法有不同的解釋或根本不支援。合成器在將 Verilog 程式碼轉換為實際電路時,需要明確的連接關係,而這種直接的階層式參考對於合成器來說通常是難以理解和實現的。
可讀性與維護性:
這種寫法降低了程式碼的可讀性。當專案變大時,很難追蹤哪個模組直接存取了另一個模組的內部訊號,這會增加維護的複雜性。
模組的封裝性:
模組應該有良好的封裝性 (encapsulation),即外部模組只能透過其定義的端口與之互動,而不應該直接進入其內部存取訊號。直接的階層式參考打破了這種封裝。
總結:
iverilog 允許你的 tb.v 程式碼正確執行,是因為它有足夠的智慧來辨識多個未被實例化的模組為頂層模組,並支援跨層次的階層式參考。
然而,在撰寫可合成的硬體描述語言時,強烈建議遵循實例化 (instantiation) 和端口連接 (port mapping) 的標準方式來建立模組之間的關係,或者使用 參數 (parameters) 或 SystemVerilog 的封裝 (packages) 來定義全域常數,以確保程式碼的清晰度、可攜性以及可合成性。
下面是我回家測試的結果
確實使用 -s 指定 top module 後,iverilog 便會報錯,可見 Bard 沒有騙人XD
沒有留言:
張貼留言