vitis HLSを使ってみた

# はじめに

vitis HLSを使ってみました。
ネット上に情報はあるんですが、現在のツール（2024.1）ではなくて、微妙に違いがあったり、自分で試してみないと分らない事も多いので、試行錯誤してみました。
この過程をメモしておきます。

# 環境

- Vitis 2024.1(windows 11 64bit版)

# 参考になりそうな公式ドキュメント

[Vitis 統合ソフトウェア プラットフォームの資料: エンベデッド ソフトウェア開発 (UG1400)
](https://docs.amd.com/r/ja-JP/ug1400-vitis-embedded)

[Vitis 高位合成ユーザー ガイド (UG1399)](https://docs.amd.com/r/ja-JP/ug1399-vitis-hls)

# 作成するもの

ARMプロセッサーに接続されたLEDを制御するモジュールを作成します。
モジュールに0,1,2,3の値を入力すると、それに応じてLEDが点灯するようにします。

# Vitis HLSでの作業

Vitis HLSを起動します。ツールの使い方の詳細は、公式ドキュメントを参照してください。

https://docs.amd.com/r/ja-JP/ug1399-vitis-hls/HLS-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90

## flow_targetの設定

flow_targetには、Vivado IP フローとVitis カーネル フローがあります。
VivadoでIPを使う方法はわかってるので、今回は、経験のあるVivado IPフローで開発してみます。
Vitis カーネル フローの方が、HLSの開発に専念できそうなので、HLSに慣れてきたら、今後試す事にします。

vitisのHSLの設定で、flow_targetを、vivadoにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56724/821bc4a6-f68a-d5d2-9375-817e489f4ddf.png)


output.formatを、ip_catalogにします。(デフォルトでこの設定になっているのかもしれませんが、私は、試行錯誤していたので、この設定になっていなくて、設定する必要がありました。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56724/7e566c3e-8af2-bc1b-b02e-43ccd891acbf.png)

## プログラムの作成

Sourcesにled.cppを追加します。
組み合わせ回路を記述してみます。
入力に与えられた値に応じて、LEDを点灯させるモジュールを作成してみます。
出力のLEDの状態は、axiのインターフェースでは取得しないので、```ap_none```を指定します。

```c:led.cpp
#include <ap_int.h>

void led(ap_uint<4> *in_data, ap_uint<4> *out) {
#pragma HLS TOP name=led
#pragma HLS INTERFACE s_axilite port=in_data
#pragma HLS INTERFACE ap_none port=out

    switch(*in_data){
        case 0:
            *out=0x1;
            break;
        case 1:
            *out=0x2;
            break;
        case 2:
            *out=0x4;
            break;
        case 3:
            *out=0x8;
            break;
        default:
            *out=0x0;
            break;
    }
}
```

テストベンチも作成してみます。

```c:led_tb.cpp
#include <ap_int.h>

void led(ap_uint<4> *in_data, ap_uint<4> *out);

int main()
{
    ap_uint<4> a, b;
    const int test_case = 4;
    int pass = 0;
    int fail = 0;

    a = 0;
    b = 0;

    for (int i = 0; i < test_case; i++) {
    	led(&a, &b);
    	if (1<<a == b) {
    		pass++;
    	} else {
    		fail++;
    	}
    	a++;
    }

    std::cout << "test_case:" <<  test_case << " pass:" << pass << " fail:" << fail << std::endl;

    if (test_case == pass) {
    	std::cout << "PASS" << std::endl;
    	return 0;
    } else {
    	std::cout << "FAIL" << std::endl;
    	return -1;
    }
}
```

Vitisのフローの`C SIMULATION` `SYNTHESIS` `C/RTL CO-SIMULATION` `PACKAGE`を順に実行します。
全て成功すればOKです。
これで、IPが作成されました。
意外と簡単にできました。

# vivadoでIPを組み込む

Vitis HLSで作成したIPが保存されているフォルダをviadoのプロジェクトに追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56724/099c3d12-9400-1766-5181-a728592abf48.png)


ブロックデザインに追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56724/dab82993-a222-803b-6203-a3ee43356750.png)

AXI Interconnectは、自動配線を実行した時に自動的に追加されます。

※ZinqプロセッサーにAXIインターフェースのマスターを有効にしておかないと、配線ができません。プロセッサーの次の設定を行っておく必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56724/2ac1ab31-d4f4-c61b-cd05-1e049f7770fa.png)

# vitisでアプリ作成

vitisでアプリを作成します。
Standaloneのアプリを作って、動作させてみます。

デバイスにアクセスする関数が用意されていています。
X{モジュール名}_Set_{パラメータ名}のような関数を呼び出すと、パラメータに値がセットされます。
今回は、in_dataに整数値を設定するとそれに応じたLEDが点灯する事になります。
0,1,2,3の値を繰り返し設定するプログラムにしてみます。

```c:main.cpp
#include "xparameters.h"
#include "xled.h"
#include "xil_printf.h" // UART出力用

// #define XPAR_LED_0_BASEADDR 0x40000000

int main() {
    // 1. デバイスインスタンス
    XLed ledDevice;
    int i,j;

    // 2. デバイスの初期化
    int status = XLed_Initialize(&ledDevice, XPAR_LED_0_BASEADDR);
    if (status != XST_SUCCESS) {
        xil_printf("Error: XLed_Initialize failed with status %d\n", status);
        return XST_FAILURE; // 初期化に失敗
    }

    xil_printf("start");
    while(1){

    for(i=0;i<4;i++){
        XLed_Set_in_data(&ledDevice, i);
        for(j=0;j<10000000;j++){
            ;
        }
    }
    }

    return 0;
}
```

# 実行結果

https://x.com/GeekMasahiro/status/1874632719256478144

想定通りにLEDが点灯しました。

# おわりに

- Vitis HLSを使って、AXI4によるIOを行うモジュールを作成してみました
- AXIインターフェースを使ったモジュールはVivadoで作るより楽
- C言語での記述を論理合成する道筋を理解できました
- vitis,vivado,vitisとツールを行き来する必要があって、少し面倒

# 今後の課題

Vitis カーネル フローも試してみたい