SystemVerilog 中的 universal verfication methodology (UVM) 是什麼(一)
SystemVerilog 中引入了 Universal Verification Methodology (UVM) 用來驗證硬體的一種手段,UVM 直翻就是通用驗證方法論,UVM 就像是一個範本,提供我們寫 testbench 的想法,並不是指使用一個特定的工具或是是語言來撰寫 testbench。上面這樣講有點抽象,打個比方來說,寫文章的方法論之一就是要有起承轉合,這根我們使用什麼樣的語言沒有關係。本文中將會從一個範例下手,討論為什麼需要 UVM。
一個可以用到 UVM 的例子
假設今天我們要作設計一個硬體 module,叫做 TopModule
好了,他會把輸入的 5-bit 整數裡面的每個 bit 1 輸出兩次,bit 0 輸出一次(從 LSB 開始)。
- 輸入資料:
5'b01001
。 - 輸出資料:
1'b1
,1'b1
,1'b0
,1'b0
,1'b1
,1'b1
,1'b0
。
假設要把 TopModule
分給兩個開發者作,一個可能的分工是:
SubmoduleA
,把 5-bit 從 LSB 開始分開輸出。- 輸入資料:
5'b01001
。 - 輸出資料:
1'b1
,1'b0
,1'b0
,1'b1
,1'b0
。
- 輸入資料:
SubmoduleB
根據收到每一個 bit 的資料輸出 1~2 bit(s)。- 輸入資料:
1'b0
或是1'b1
。 - 輸出資料:兩種情形分別是
1'b0
或是兩次1'b1
。
- 輸入資料:
(註:實際上一個這麼簡單的 module 應該是一個開發者用一個 module 就寫完了,這邊是為了舉例方便。)
除了 module 的設計之外,一般會拿高階語言來實做 golden reference 作為 Verilog 的驗證標準。例如,下面是用 Python 對這三個 module 的相同實做:
1def SubmoduleA(x: int): -> list:
2 # 0b01001 -> [1,0,0,1,0]
3 ret = list()
4 for i in range(5):
5 ret.append(x&1)
6 x >>= 1
7 return ret
8
9def SubModuleB(x: int): -> list:
10 # 0 -> [0]
11 # 1 -> [1,1]
12 return [1,1] if x == 1 else [0]
13
14def TopModule(x: int):list:
15 stage1 = SubmoduleA(x)
16 stage2 = [SubmoduleB(i) for i in stage1]
17 return sum(stage2)
“Universal” 之處!
上面的例子看起來稀鬆平常,到底哪裡有跟 universal, methodology 這兩個字眼有關係呢?作者自己認為是,當我們統一輸出/輸入資料的格式,將能對驗證帶來很大的好處,從上面的文章中目前看得出來的是以下這兩點:
TopModule
,SubmoduleA
,SubmoduleB
中,統一使用輸入/輸出資料描述其規格。- Python 中,所有的 function 統一為
(int)->list
的格式,該int
為輸入資料,list
為輸出資料 的格式。
輸出端常常被稱為 master (M),輸入端常常被稱為 slave (S),畫成圖片的話就會變成:
Universal Testbench
當我們要分別驗證這些 module 的時候,我們就會寫出三個 testbench,其架構如下:
嗯,你說我上面圖片是不是複製貼上?當然是啊,要不然怎麼叫做 universal,統一界面的最終目的就是要用一份 code 就能驗證所有的 module 啊!
那麼,generic master/slave 要怎麼作呢?因為我們有跟 Verilog 一致的 Python 實做,只要用 Python dump 出資料寫到檔案裡面,那麼 generic master 就只要把 dump 讀進來,從 master 把資料送出。generic 就是檢查 slave 收到的資料順序跟 dump 是否一致送到。因此,所有的 testbench 都只要包括這三點主要內容:
- 宣告 generic master/slave 各一。
- 接線。
- 指定 generic master/slave 要使用的 trace 檔案。
上述用 trace 的作法雖然簡單,然而對於比較大的系統,光是一個 trace 的資料可能能輕易到達 TB 等級,如果驗證真實系統甚至要跟週邊硬體互動,要預先生成這些成 trace 通常不是很容易。更現代的 UVM 環境通常都會包括了能動態產生資料的功能,這個就留在之後的文章討論吧。
除此之外,更現代的 UVM 環境中,甚至可以作到自動化。透過固定 Python/Verilog module 的輸入/輸出資料格式,每個 testbench 的行為都非常接近。只要把兩者 RTL 丟進去 tool,就能自動跑驗證。甚至不太需要寫 testbench。所以上面的例子中,最簡單的情形下,只需要指定說要用 Python 的三個函數來驗證 Verilog 中同名的三個 module 就能完成驗證了,大大的省去了驗證時間。
文章到這邊,作者少提到了一個東西,那就是本文前面統一的輸入/輸出資料的部份中,並沒有描述底層 Verilog 實做時的細節。以下的文章將會把這部份補完,有興趣的就往下一段看吧。
(補充章節)統一 RTL 中輸入/輸出資料之 IO port
在硬體底層一個相當好用、簡易的界面是用額外兩條信號線來控制資料 data
的輸出(或是暫停輸出)的時序,一般會稱為 valid
跟 ready
。(因為用了兩條信號線,作者自己叫他 2-wire protocol,網路上找不到統一的稱呼。)
信號名稱 | 信號方向 |
---|---|
valid |
M→S |
ready |
S→M |
data |
M→S |
這兩條信號線
valid |
ready |
這個 cycle 之意義 |
---|---|---|
1’b0 | 1’bx | 沒有資料要傳輸 |
1’b1 | 1’b0 | Master 想要傳輸 data ,但是 slave 無法收,因此 master 要把資料維持到下一個 cycle。 |
1’b1 | 1’b1 | Master 成功傳輸 data 給 slave,該傳輸完成下一個 cycle可以開始另外一筆傳輸。 |
下面這張圖顯示了「由 master 向傳輸了三次資料」的時序圖:
2-wire protocol 因為其簡易性,許多進階的 bus protocol (AXI, Avalon…) 也都是基於 2-wire 定義出來的,本文中使用 valid
跟 ready
也是遵照 AXI 的稱呼。