SystemVerilog 中的 universal verfication methodology (UVM) 是什麼(一)

Share on:

本文內容採用創用 CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款授權.

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 都只要包括這三點主要內容:

  1. 宣告 generic master/slave 各一。
  2. 接線。
  3. 指定 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 的輸出(或是暫停輸出)的時序,一般會稱為 validready。(因為用了兩條信號線,作者自己叫他 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 定義出來的,本文中使用 validready 也是遵照 AXI 的稱呼。