Vivado HLSテストベンチでは乱数シードは固定にするべき

# はじめに
皆さんこんにちは．[このあいだのVivado HLSの記事](https://qiita.com/HoriThe3rd/items/7d9025342cfc55454046)で別にまとめると言っていた記事です．
飛行機に乗り遅れたので空港で書いています(泣)

Vivado HLSでテストベンチを記述するとき，自分の作った関数が正しく動くか検証するために，引数に乱数を使って検証したいときってありますよね．イメージとしては以下のような感じです．

![vivado_hlsテストベンチの流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357417/510b3e98-d6a3-e7a3-d11f-f8ac270e3a1d.png)

本記事の内容は，Vivado HLSのテストベンチで乱数を生成する関数のシードには固定値を入れてあげないと失敗する，と言うことの紹介です．これにはまって小一時間溶かしましたので，同じ思いをする人が減ると幸いです．

# 環境
私が使用した環境は以下の通りです．

- Vivado HLS 2018.3 (Linux版)
- OS: CentOS 7, 64bit

# テストベンチコード
[前の記事](https://qiita.com/HoriThe3rd/items/7d9025342cfc55454046)で作成した行列演算関数に与える引数を乱数で生成して，テストするコードを作成しました．乱数はC++のrandomヘッダを用いて生成しています．
しかし，これを実行すると，C/RTL Cosimulationでコンパイルなどは通るものの，テスト失敗に終わります．

```cpp
#include <iostream>
#include <random>
#include "Eigen/Core"
#include "Eigen/Geometry"

using namespace std;

#define SIZE_ROW 3
#define SIZE_COL 4

void dot_multi(int w[SIZE_ROW][SIZE_COL], int v[SIZE_COL], int o[SIZE_ROW]);

int main()
{
    cout << "start" << endl;

    // ここで乱数生成機を作成している
    random_device rng;
    mt19937_64 mt(rng());
    uniform_int_distribution<> rng10(0, 10);

    int w[SIZE_ROW][SIZE_COL];
    int v[SIZE_COL];
    int result[SIZE_ROW];

    Eigen::MatrixXi ew = Eigen::MatrixXi::Zero(SIZE_ROW, SIZE_COL);
    Eigen::VectorXi ev = Eigen::VectorXi::Zero(SIZE_COL);
    Eigen::VectorXi eresult = Eigen::VectorXi::Zero(SIZE_ROW);

    // Initialize
    for(int i=0; i<SIZE_ROW; i++){
        for(int j=0; j<SIZE_COL; j++){
            int num = rng10(mt);
            w[i][j] = num;
            ew(i,j) = num;
        }
    }
    for(int i=0; i<SIZE_COL; i++){
        int num = rng10(mt);
        v[i] = num;
        ev(i) = num;
    }

    // 自作の関数を実行
    dot_multi(w, v, result);
    // テストで比較するためにEigenでも同様の計算を実行
    eresult = ew * ev;

    // Display arguments
    cout << "--- w ---" << endl;
    for(int i=0; i<SIZE_ROW; i++){
        for(int j=0; j<SIZE_COL; j++){
            cout << w[i][j] << " ";
        }
        cout << endl;
    }
    cout << "--- v --- " << endl;
    for(int i=0; i<SIZE_COL; i++){
        cout << v[i] << endl;
    }
    cout << "--- Eigen w ---" << endl;
    cout << ew << endl;
    cout << "--- Eigen v ---" << endl;
    cout << ev << endl;

    // Display the result
    cout << " --- Result --- " << endl;
    for(int i=0; i<SIZE_ROW; i++){
        cout << result[i] << endl;
    }
    cout << " --- Eigen Result --- " << endl;
    cout << eresult << endl;

    // Auto check
    for(int i=0; i<SIZE_ROW; i++){
        if(result[i] != eresult(i)){
            cout << "**** Test failed... :( ****" << endl;
            return 1;
        }
    }
    cout << "*** Test passed ***" << endl;
    return 0;
}
```

# 原因
C/RTL Cosimulationを実行する際，次のような手順で実行されるようです．

1. テストベンチコードを一度実行し，関数に与える入力データを作成
2. RTLになった検証対象の関数に入力データを与え，戻り値を得る
3. 再度テストベンチコードを実行し，検証対象の関数の戻り値を正解と比較する

このことから，テストベンチコードは２回実行されるようです．実際に，coutなどでコンソールに文字を表示してみると，２回表示されるはずです．

つまり，乱数のシードをrandom_deviceで生成される非決定的乱数としたとき，1で検証対象の関数に与えられる引数と，3でテストベンチの検証用関数に与えられる引数が違った値となり，それぞれの関数が違った値をかえしたことになり，テストに失敗します．

なお，乱数を用いることについて，XilinxのユーザーガイドUG90[^1]2の"C/RTL強調シミュレーションのデバッグ"の項目にも実は記述がありました．次のように記載されています．

[^1]: [Vivado Design Suite ユーザーガイド　高位合成（UG902）](https://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2018_3/ug902-vivado-high-level-synthesis.pdf)

>乱数生成に決まった (乱数ではない) シードを使用するようにテストベンチを変更します。乱数生成のシードがタイム ベースシードなどの変数に基づいている場合、シミュレーションに使用されるデータがテストベンチを実行するたびに異なり、結果が異なってしまいます。

乱数のシードには固定値を使いましょうと明記されていました．

# 対策
対策として，実行ごとに変化する乱数を用いてテストベンチを作成したのがいけないので，実行ごとに同じ値が出力されるようにすればOKです．具体的には，乱数生成機に与えるシード値を固定値にすれば実現できます．
例えば先に示したコードだと，次のようになります．

```cpp
    // ここで乱数生成機を作成している
    // シード値を乱数とするとダメ
    //random_device rng;
    // シード値を固定値にする
    mt19937_64 mt(123);
    uniform_int_distribution<> rng10(0, 10);
```

このような書き方でいいのか気になりますが，シードを固定にすれば問題なくテストにパスできました．

# 終わりに
巨大な配列を引数にして計算させる関数など，入力値を自作するのが面倒くさいときに乱数で自動化するのは便利です．また，シード値一つ変更するだけですべての値を変えることができる点も，便利かと思います．

Vivado HLSで乱数を使うときは，シードは固定にしましょう．
また，Xilinxのユーザーガイドはボリュームがあって読むのが大変ですが，困ったら目を通すといいかもしれません．