Hello Verilator—高品質&開源的 SystemVerilog(Verilog) 模擬器介紹&教學(二)

Share on:

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

在前面一篇文章中分享了作者使用 verilator 來模擬數位 IC 的經驗。Verilator 具有對新語法的支援度、相當好的可靠度,速度甚至在商用工具之上,這篇文章將會用個簡單的案例分享 verilator 的用法。

Hello Verilator

在今天的目標中,我們要寫出一個 hello world Verilog module,其會花費 5 個 cycle 用 ascii 連續輸出 8-bit data"hello" ,並且在這五個 cycle 把 valid 拉高表示輸出有效:

上一篇文章中提及,要用 verilator 驗證 Verilog 需要的步驟如下:

  1. 撰寫 testbench testbench.cpp
  2. 準備可合成之待測模組 design_under_test.v
  3. 把待測試模組轉換成 C++(或是 SystemC),並指名要使用的 testbench verilator design_under_test.v --exe testbench.cpp --cc
  4. Verilator 產生的檔案都在 obj_dir 資料夾(這個是預設名稱)。
  5. 在該資料夾下面編譯產生 binary make -C obj_dir -f Vdesign_under_test.mk
  6. 執行模擬 ./obj_dir/Vdesign_under_test

接下來的文章中,將會執行一次這些流程,並確定我的們的待測模組 degisn_under_test 真的有輸出想要的 waveform。

準備 Verilog

可以用下面的 Verilog 實做以輸出上面要求的波型,因為撰寫 Verilog 本身不是今天要討論的範圍,就不多作解釋如何實做了,以下是今天的 design_under_test.sv

 1module design_under_test(
 2    input clk, input rst,
 3    output logic valid,
 4    output logic [7:0] data
 5);
 6    logic [39:0] out_all;
 7    always_comb begin
 8        data = out_all[7:0];
 9        valid = (data != '0);
10    end
11
12    always_ff @(posedge clk or negedge rst) begin
13        if (!rst) begin
14            out_all <= "olleH";
15        end else if (valid) begin
16            out_all <= out_all >> 8;
17        end
18    end
19endmodule

準備 testbench

我們暫時先拿一個空的 testbench.cpp 來跑整個流程。

1int main() {
2    return 0;
3}
把上面的步驟 3-6 跑一次就好了,執行完畢之後會發現……什麼都沒有輸出。這是正常的現象,因為 main() 裡面什麼都沒有跑,接下來我們就要慢慢把 testbench 填滿。

準備 testbench(續)

首先先檢查 obj_dir/Vdesign_under_test.h,裡面是一堆亂七八糟的 code 都看不懂,但是可以看到開頭的前幾行是這樣:

1VL_MODULE(Vdesign_under_test) {
2public:
3    VL_IN8(clk,0,0);
4    VL_IN8(rst,0,0);
5    VL_OUT8(valid,0,0);
6    VL_OUT8(data,7,0);
把 macro 展開之後,其實就是這樣:
1class Vdesign_under_test {
2public:
3    uint8_t clk;
4    uint8_t rst;
5    uint8_t valid;
6    uint8_t data;
7};
這四個正好就是 Verilog module 的所有 IO,不多也不少。我們要測試 design_under_test 時,也只需要用到這四個界面。例如下面就是模擬對 design_under_test 作 reset 之後跑 20 個 cycle:
 1#include "Vdesign_under_test.h"
 2#include <memory>
 3#include <iostream>
 4int main(int, char**)
 5{
 6    std::unique_ptr<Vdesign_under_test> dut(new Vdesign_under_test);
 7    dut->clk = 1; dut->rst = 1; dut->eval();
 8    dut->rst = 0; dut->eval();
 9    dut->rst = 1; dut->eval();
10    for (int i = 0; i < 40; ++i) {
11        dut->clk = 1 - dut->clk; dut->eval();
12    }
13    return 0;
14}
跟用 verilog 寫 testbench 最大的不一樣的一點是,每次更改值都需要 dut->eval() 自己讓模擬器知道需要前進一個 timestamp。

最後我們只要在迴圈中加上「如果 valid == 1 則把 data 印出來」的程式碼,注意我們只在 dut->clk == 0 的時候判斷,這個是在 clk 原本是 1 變成之後呼叫 eval(),也就是在 negedge sample validdata 值的意思。

1for (int i = 0; i < 40; ++i) {
2    dut->clk = 1 - dut->clk; dut->eval();
3    if (dut->clk == 0 and dut->valid == 1)
4        std::cout << char(dut->data) << std::endl;
5}
重新跑一次步驟 5-6,可以看到輸出如下,也就是我們預期的結果:
1H
2e
3l
4l
5o

以下廢話

前面一篇文章提到了,因為作者最近買了文明帝國,所以這篇被拆出成另外一篇了。可是一般來說買了文明帝國不可能在第二天就生出一篇文章,到底是發生什麼事情呢?原因是因為作者技術太差被電腦狂電,一怒之下就把遊戲關了。