|
|
# Verilatorを用いた高速シミュレーション
|
|
|
|
|
|
最終課題である CoreMark は非常に命令数が多いため、シミュレーションに時間がかかります。
|
|
|
また、命令数も非常に多いため、Vivado の波形を眺めてデバッグするのは困難です。
|
|
|
Verilatorという高速なシミュレータを用いてデバッグしましょう。
|
|
|
|
|
|
## Verilatorとは
|
|
|
|
|
|
VerilatorはオープンソースのVerilog HDLシミュレータです。
|
|
|
|
|
|
Verilog HDLの記述をC++に変換し、コンパイル後にシミュレーションするため非常に高速という特徴があります。
|
|
|
一方、高速化のためにVerilog HDLの一部の機能が使えないという制約があります。
|
|
|
|
|
|
また、テストベンチをC++で書く必要があります。
|
|
|
|
|
|
## Verilatorのインストール
|
|
|
|
|
|
```
|
|
|
$ sudo apt-get install verilator
|
|
|
```
|
|
|
|
|
|
高速化のため、以下の設定をおすすめします(編集にはroot権限が必要です)。
|
|
|
|
|
|
* `/usr/share/verilator/include/verilated.mk`の89行目`#OPT_FAST = -O2 -fstrict-aliasing`の`#`を消すことで最適化を有効にする
|
|
|
|
|
|
## Verilatorを用いてCoreMark_for_Synthesisのシミュレーションを行う
|
|
|
|
|
|
適当な作業ディレクトリを用意し、そこにVerilog HDLのソースファイルを置いたとします。
|
|
|
|
|
|
そのディレクトリに、C++で書かれたテストベンチファイルを作成します。例を一番下に書きました。
|
|
|
|
|
|
トップモジュールが`top.v`の場合、`$ verilator --cc top.v -exe test_bench.cpp`とコマンドを実行することで、`obj_dir`というディレクトリが作成されます(Vivadoでは出ないLintエラーの警告が出るかもしれません)。このディレクトリの中には、C++に変換された`top.v`である`Vtop.h`などが含まれています(`cpu.v`であれば`Vcpu.h`ができるなど、ファイル名に応じて生成されるファイル名が変化します。適宜読み替えてください)。
|
|
|
|
|
|
なお、トップモジュールから参照されているモジュールはファイル名ベースで検索されます。Verilog HDLの一般的な慣習に従い、モジュールの記述は同名のファイル(`alu`モジュールなら`alu.v`)に書きましょう。
|
|
|
|
|
|
`$ make -C obj_dir -f Vtop.mk`とコマンドを実行することで、`obj_dir`の中に`Vtop`という実行ファイルが作成されます。
|
|
|
これを実行する(`$ obj_dir/Vtop`)ことで、シミュレーションが行われます。
|
|
|
|
|
|
以下は、C++で書かれたテストベンチ(`test_bench.cpp`)の例です。
|
|
|
|
|
|
```cpp
|
|
|
#include <cstdio>
|
|
|
#include <verilated.h>
|
|
|
#include "Vtop.h"
|
|
|
|
|
|
// Set the clock speed of your processor.
|
|
|
static constexpr std::size_t clock_Hz = 100000000;
|
|
|
// UART baudrate
|
|
|
static constexpr std::size_t uart_Hz = 115200;
|
|
|
// The number of CoreMark iterations is depend on clock speed.
|
|
|
// Max: 30 seconds
|
|
|
static constexpr std::size_t max_cycle = 30 * clock_Hz;
|
|
|
|
|
|
std::size_t timer_ps = 0;
|
|
|
|
|
|
void uart_rx(unsigned int u) {
|
|
|
static std::size_t s = 0;
|
|
|
static std::size_t b = 0;
|
|
|
static char c = 0;
|
|
|
if( s == 0 && u == 0 ) {
|
|
|
s = timer_ps;
|
|
|
b = 0;
|
|
|
c = 0;
|
|
|
} else if( s != 0 && s + 1000000000000 / uart_Hz / 2 * (2*b+3) < timer_ps ) {
|
|
|
if( b < 8 ) {
|
|
|
c += u << b;
|
|
|
++b;
|
|
|
} else {
|
|
|
std::putchar(c);
|
|
|
std::fflush(stdout);
|
|
|
s = 0;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
Vtop top;
|
|
|
top.sysclk = 0;
|
|
|
top.eval();
|
|
|
top.cpu_resetn = 0;
|
|
|
top.eval();
|
|
|
top.sysclk = 1;
|
|
|
top.eval();
|
|
|
top.sysclk = 0;
|
|
|
top.eval();
|
|
|
top.cpu_resetn = 1;
|
|
|
top.eval();
|
|
|
|
|
|
for( std::size_t cycle = 0; cycle < max_cycle; ++cycle ) {
|
|
|
top.sysclk = 0;
|
|
|
top.eval();
|
|
|
top.sysclk = 1;
|
|
|
top.eval();
|
|
|
uart_rx(top.uart_rx_out);
|
|
|
timer_ps += 1000000000000 / clock_Hz;
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
`readmemh`で読み込まれるhexファイルのサイズがVerilog HDLで記述されたメモリの容量より多い場合、Vivadoでは入りきらない部分が無視されるだけですが、Verilatorでは実行時エラーとなります。hexファイルのサイズをVerilog HDLで記述されたメモリの容量と一致させるようにしてください。 |
|
|
# Verilatorを用いた高速シミュレーション
|
|
|
|
|
|
最終課題である CoreMark は非常に命令数が多いため、シミュレーションに時間がかかります。
|
|
|
また、命令数も非常に多いため、Vivado の波形を眺めてデバッグするのは困難です。
|
|
|
Verilatorという高速なシミュレータを用いてデバッグしましょう。
|
|
|
|
|
|
## Verilatorとは
|
|
|
|
|
|
VerilatorはオープンソースのVerilog HDL/SystemVerilogシミュレータです。Verilog HDL/SystemVerilogの記述をC++に変換し、コンパイル後にシミュレーションするため非常に高速という特徴があります。一方、高速化のためにVerilog HDL/SystemVerilogの一部の機能が使えないという制約があります。
|
|
|
|
|
|
現在はテストベンチをC++で書く必要があります。
|
|
|
|
|
|
## Verilatorのインストール
|
|
|
|
|
|
```
|
|
|
$ sudo apt-get install verilator
|
|
|
```
|
|
|
|
|
|
高速化のため、以下の設定をおすすめします(編集にはroot権限が必要です)。(2024追記:行数は変わっている様です。`OPT_FAST`をエディタ内部で検索するとよいでしょう)
|
|
|
|
|
|
* `/usr/share/verilator/include/verilated.mk`の89行目`#OPT_FAST = -O2 -fstrict-aliasing`の`#`を消すことで最適化を有効にする
|
|
|
|
|
|
## Verilatorを用いてCoreMark_for_Synthesisのシミュレーションを行う
|
|
|
|
|
|
適当な作業ディレクトリを用意し、そこにVerilog HDLのソースファイルを置いているとします。
|
|
|
そのディレクトリに、C++で書かれたテストベンチファイルを作成します。例を一番下に書きました。
|
|
|
|
|
|
Verilatorは先で述べたとおり、CPPを利用したシミュレータです。このためシミュレーションでは
|
|
|
1. verilog -> c++へのトランスパイル(verilatorコマンド)
|
|
|
2. c++のビルド(obj_dir内部でのmake)
|
|
|
3. 実行
|
|
|
|
|
|
の三段階のステップが必要になります。以下が詳細です。
|
|
|
|
|
|
### 1. c++へのトランスパイル
|
|
|
トップモジュールが`top.v`の場合、`$ verilator --cc top.v -exe test_bench.cpp`とコマンドを実行することで、`obj_dir`というディレクトリが作成されます(Vivadoでは出ないLintエラーの警告が出るかもしれません)。このディレクトリの中には、C++に変換された`top.v`である`Vtop.h`などが含まれています(`cpu.v`であれば`Vcpu.h`ができるなど、ファイル名に応じて生成されるファイル名が変化します。適宜読み替えてください)。
|
|
|
|
|
|
なお、トップモジュールから参照されているモジュールはファイル名ベースで検索されます。Verilog HDLの一般的な慣習に従い、モジュールの記述は同名のファイル(`alu`モジュールなら`alu.v`)に書きましょう。
|
|
|
|
|
|
### 2. c++のビルド
|
|
|
`$ make -C obj_dir -f Vtop.mk`とコマンドを実行することで、`obj_dir`の中に`Vtop`という実行ファイルが作成されます。
|
|
|
|
|
|
### 3. 実行
|
|
|
実行する(`$ obj_dir/Vtop`)ことで、シミュレーションが行われます。
|
|
|
|
|
|
## テストベンチの例
|
|
|
以下は、C++で書かれたテストベンチ(`test_bench.cpp`)の例です。
|
|
|
トップモジュールの入出力がVtopのメンバ変数になります。inputに対応するメンバ変数を変更し、eval関数を実行することでoutputが正しい値になります。
|
|
|
なお、正しく配線がつながっていない場合はoutputがおかしな値になります。
|
|
|
|
|
|
```cpp
|
|
|
#include <cstdio>
|
|
|
#include <verilated.h>
|
|
|
#include "Vtop.h"
|
|
|
|
|
|
// Set the clock speed of your processor.
|
|
|
static constexpr std::size_t clock_Hz = 100000000;
|
|
|
// UART baudrate
|
|
|
static constexpr std::size_t uart_Hz = 115200;
|
|
|
// The number of CoreMark iterations is depend on clock speed.
|
|
|
// Max: 30 seconds
|
|
|
static constexpr std::size_t max_cycle = 30 * clock_Hz;
|
|
|
|
|
|
std::size_t timer_ps = 0;
|
|
|
|
|
|
void uart_rx(unsigned int u) {
|
|
|
static std::size_t s = 0;
|
|
|
static std::size_t b = 0;
|
|
|
static char c = 0;
|
|
|
if( s == 0 && u == 0 ) {
|
|
|
s = timer_ps;
|
|
|
b = 0;
|
|
|
c = 0;
|
|
|
} else if( s != 0 && s + 1000000000000 / uart_Hz / 2 * (2*b+3) < timer_ps ) {
|
|
|
if( b < 8 ) {
|
|
|
c += u << b;
|
|
|
++b;
|
|
|
} else {
|
|
|
std::putchar(c);
|
|
|
std::fflush(stdout);
|
|
|
s = 0;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
Vtop top;
|
|
|
top.sysclk = 0;
|
|
|
top.eval();
|
|
|
top.cpu_resetn = 0;
|
|
|
top.eval();
|
|
|
top.sysclk = 1;
|
|
|
top.eval();
|
|
|
top.sysclk = 0;
|
|
|
top.eval();
|
|
|
top.cpu_resetn = 1;
|
|
|
top.eval();
|
|
|
|
|
|
for( std::size_t cycle = 0; cycle < max_cycle; ++cycle ) {
|
|
|
top.sysclk = 0;
|
|
|
top.eval();
|
|
|
top.sysclk = 1;
|
|
|
top.eval();
|
|
|
uart_rx(top.uart_rx_out);
|
|
|
timer_ps += 1000000000000 / clock_Hz;
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
`readmemh`で読み込まれるhexファイルのサイズがVerilog HDLで記述されたメモリの容量より多い場合、Vivadoでは入りきらない部分が無視されるだけですが、Verilatorでは実行時エラーとなります。hexファイルのサイズをVerilog HDLで記述されたメモリの容量と一致させるようにしてください。
|
|
|
|
|
|
## 注意
|
|
|
uartを正しくシミュレーションするためにはuart.vの周波数と、c++の周波数定義が一致している必要があります。 |