makeコマンドでVivado HLSを使おう

本記事はFPGA Advent Calendar 2019の14日目です．
昨日は[nv-h](https://qiita.com/nv-h)さんの[個人的によく使うベンダー(Xilinx, Intel)依存のHDL attribute](https://qiita.com/nv-h/items/3968f033404ca7e3704b)でした．

Vivado HLSで色んなIPを開発していると，段々と思いませんか？
『シミュレーションや高位合成の為だけにGUIのボタンをポチポチしたくない...』
そんなことを思った私はmakeコマンドだけで開発したくなり，必要なMakefileとtclスクリプトを生成するためのツール[vivado_hls_create_project](https://github.com/Kenta11/vivado_hls_create_project)を公開しました．
今回はそんなツールの使い方について紹介します．

# 環境

ツールの動作確認は以下の環境で行いました．

- OS: Ubuntu 16.04 LTS, 18.04 LTS, ManjaroLinux
- Vivado: 2018.1, 2018.2, 2018.3, 2019.1

# インストールとセットアップ

Ubuntu 18.04 LTS上で開発する場合を前提としていきます．
まずはgit, make, pipをインストールします．

```
$ sudo apt install git build-essential python3-pip
```

pipでvivado_hls_create_projectをインストールしましょう．

```
$ sudo pip3 install git+https://github.com/Kenta11/vivado_hls_create_project
(中略)
$ vivado_hls_create_project --version
v3.3.3
$ vivado_hls_create_project --help
Usage: vivado_hls_create_project [-h|--help] <command> [<args>]

Makefile and tcl scripts generator for Vivado HLS

<command>:
    list         list available boards
    create       create Makefile and tcl scripts
    set-config   set parameter into ~/.vivado_hls_create_project
    get-config   get parameter from ~/.vivado_hls_create_project

optional arguments:
  -h, --help     show this help message and exit
```

続いて使用したいVivadoのパスを設定しましょう．

```
$ vivado_hls_create_project set-config path_to_vivado /path/to/Vivado/20xx.x/
$ vivado_hls_create_project get-config path_to_vivado
/path/to/Vivado/20xx.x/
```

設定はJSON形式で`~/.vivado_hls_create_project`に書かれるので，ファイルを直接編集してもOKです．

```
$ cat ~/.vivado_hls_create_project 
{"path_to_vivado": "/path/to/Vivado/20xx.x"}
```

本コマンドがサポートしているFPGAボードの一覧を表示します．

```
$ vivado_hls_create_project list
Board               | Part                          
--------------------|-------------------------------
ac701               | xc7a200tfbg676-2              
arty                | xc7a35ticsg324-1L             
arty-a7-100         | xc7a100tcsg324-1              
arty-a7-35          | xc7a35ticsg324-1L             
arty-s7-25          | xc7s25csga324-1               
arty-s7-50          | xc7s50csga324-1               
arty-z7-10          | xc7z010clg400-1               
arty-z7-20          | xc7z020clg400-1               
basys3              | xc7a35tcpg236-1               
cmod-s7-25          | xc7s25csga225-1               
cmod_a7-15t         | xc7a15tcpg236-1               
cmod_a7-35t         | xc7a35tcpg236-1               
cora-z7-07s         | xc7z007sclg400-1              
cora-z7-10          | xc7z010clg400-1               
eclypse-z7          | xc7z020clg484-1               
genesys2            | xc7k325tffg900-2              
kcu116              | xcku5p-ffvb676-2-e            
nexys-a7-100t       | xc7a100tcsg324-1              
nexys-a7-50t        | xc7a50ticsg324-1L             
nexys4              | xc7a100tcsg324-1              
nexys4_ddr          | xc7a100tcsg324-1              
nexys_video         | xc7a200tsbg484-1              
sp701               | xc7s100fgga676-2              
sword               | xc7k325tffg900-2              
zc702               | xc7z020clg484-1               
zcu104              | xczu7ev-ffvc1156-2-e          
zcu106              | xczu7ev-ffvc1156-2-e          
zed                 | xc7z020clg484-1               
zedboard            | xc7z020clg484-1               
zybo                | xc7z010clg400-1               
zybo-z7-10          | xc7z010clg400-1               
zybo-z7-20          | xc7z020clg400-1
```

他に使いたいボードがある場合は`/path/to/Vivado/20xx.x/data/boards/board_files/`下にボードファイルを置いて下さい．

# それでは開発

試しに簡単なIPを開発してみましょう．

まずはプロジェクトを生成します．

```
$ vivado_hls_create_project create --help
Usage: vivado_hls_create_project create [arguments] <project_name>

Makefile and tcl scripts generator for Vivado HLS

arguments:
  -b, --board <board_name>                   set board name
  <project_name>                             set project name

optional arguments:
  -h, --help                                 show this help message and exit
  -s, --solution <solution_name>             set solution name
  -c, --clock <clock>                        set clock frequency of module
  --compiler_argument <compiler_argument>    set arguments for compiler
  --linker_argument <linker_argument>        set arguments for linker

$ vivado_hls_create_project create --compiler_argument -std=c++11 -b zybo-z7-20 sample
[I 191214 22:19:11 generator:36] generate Makefile
[I 191214 22:19:11 generator:50] generate tcl scripts
[I 191214 22:19:11 generator:95] generate directives.tcl
[I 191214 22:19:11 generator:100] generate .gitignore
[I 191214 22:19:11 generator:113] generate source code
$ tree sample
sample
├── Makefile
├── directives.tcl
├── include
│   └── sample.hpp
├── script
│   ├── cosim.tcl
│   ├── csim.tcl
│   ├── csynth.tcl
│   ├── export.tcl
│   └── init.tcl
├── src
│   └── sample.cpp
└── test
    ├── include
    └── src
        └── test_sample.cpp

6 directories, 10 files
```

`include/sample.hpp`, `src/sample.cpp`がそれぞれ合成ターゲットとする関数用のファイルです．
sample()に高位合成したい回路のロジックを書いていきましょう．

- include/sample.hpp

```
#ifndef _SAMPLE_HPP_
#define _SAMPLE_HPP_

void
sample(
    
);

#endif  // _SAMPLE_HPP_
```

- src/sample.cpp 

```
#include "sample.hpp"

void
sample(
    
){

}
```

`test/src/test_sample.cpp`はテストベンチです．C simulationのコードはこのファイルのmain()内に書きましょう．

入力の冪を出力するIPを開発してみます．

- include/sample.hpp

```
#ifndef _SAMPLE_HPP_
#define _SAMPLE_HPP_

#include <cstdint>

#include "hls_stream.h"

void
sample(
    hls::stream<uint32_t> &dina,
    hls::stream<uint32_t> &dinb,
    hls::stream<uint32_t> &dout
);

#endif  // _SAMPLE_HPP_
```

- src/sample.cpp

```
#include "sample.hpp"

void
sample(
    hls::stream<uint32_t> &dina,
    hls::stream<uint32_t> &dinb,
    hls::stream<uint32_t> &dout
){
    uint32_t a, b, ret;

    dina >> a; dinb >> b;
    ret = 1;

    for (int i = 0; i < b; i++) {
        ret *= a;
    }

    dout << ret;
}
```

- test/src/sample.cpp

```
#include "sample.hpp"

#include <cmath>

int
main(int argc, const char **argv) {
    hls::stream<uint32_t> dina, dinb, dout;
    for (int i = 0; i < 10; i++) {
        dina << i+1;
        dinb << i;
    }

    for (int i = 0; i < 10; i++) {
        sample(dina, dinb, dout);
    }

    for (int i = 0; i < 10; i++) {
        std::cout << dout.read() << ", " << (uint32_t)std::pow(i+1, i) << std::endl;
    }

    return 0;
}
```

`make csim`でC simulationを開始します．なおこのとき利用されるVivado HLSは`~/.vivado_hls_create_project`に設定したパスの先にあるものです．

```
$ make csim
/path/to/Vivado/20xx.x/bin/vivado_hls script/csim.tcl

****** Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC v20xx.x (64-bit)
  **** SW Build 2552052 on Fri May 24 14:47:09 MDT 2019
  **** IP Build 2548770 on Fri May 24 18:01:18 MDT 2019
    ** Copyright 1986-2019 Xilinx, Inc. All Rights Reserved.

(中略)

1, 1
2, 2
9, 9
64, 64
625, 625
7776, 7776
117649, 117649
2097152, 2097152
43046721, 43046721
1000000000, 1000000000
INFO: [SIM 211-1] CSim done with 0 errors.
INFO: [SIM 211-3] *************** CSIM finish ***************
INFO: [Common 17-206] Exiting vivado_hls at Sat Dec 14 22:38:41 2019...
touch sample/sample/csim
```

上手くシミュレーション出来ました．`make`で高位合成してみます．

```
$ make
/path/to/Vivado/20xx.x/bin/vivado_hls script/csynth.tcl

****** Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC v20xx.x (64-bit)
  **** SW Build 2552052 on Fri May 24 14:47:09 MDT 2019
  **** IP Build 2548770 on Fri May 24 18:01:18 MDT 2019
    ** Copyright 1986-2019 Xilinx, Inc. All Rights Reserved.

(中略)

INFO: [IP_Flow 19-234] Refreshing IP repositories
INFO: [IP_Flow 19-1704] No user IP repositories specified
INFO: [IP_Flow 19-2313] Loaded Vivado IP repository '/path/to/Xilinx/Vivado/20xx.x/data/ip'.
INFO: [Common 17-206] Exiting Vivado at Sat Dec 14 22:41:33 2019...
INFO: [Common 17-206] Exiting vivado_hls at Sat Dec 14 22:41:33 2019...
```

IPが出力できました！

# おわりに

vivado_hls_create_projectの使い方を紹介しました．もしよければ使ってみて下さい．