LoginSignup
11
12

More than 5 years have passed since last update.

Vivado HLSで、世界のナベアツ (3の倍数と3のつく数字の時だけアホになるIP) を作る

Last updated at Posted at 2018-01-22

この記事について

Vivado HLSを使用して、C言語だけで、3の倍数と3のつく数字の時だけアホになるIPを高位合成して作ります。

タイトルはネタですが、ハンズオンっぽく、Vivado HLSの使い方を習得できるような記事にしたつもりです。

全体の仕様

  • CPU (Cアプリケーション)からナベアツIPのレジスタに数字を書く。
  • ナベアツIPは、アホになるときにLEDを点灯する。また、アホかどうかをレジスタに書く。CPUはその値を確認できる。

ナベアツIPの仕様

  • 名前: nabeatsu
  • 入力ポート: ap_uint<32> number
    • 入力となる数字
    • インターフェイス = AXI Lite スレーブ
  • 出力ポート: ap_uint<1> *ahoOut
    • アホになるとき1を出力
    • インターフェイス = ap_none (+ register)
  • 出力ポート: ap_uint<1> 戻り値
    • アホになるとき1を出力
    • インターフェイス = AXI Lite スレーブ

出力ポートが2つあるのは、CPUがAXI経由でレジスタからREADする用、と、LEDに直接出力する用です。

環境と事前知識

環境

  • 開発用PC: Windows 10 64-bit
    • Vivado 2017.4 WebPACKライセンス
    • Vivado HLS 2017.4
    • Xilinx SDK 2017.4
  • ターゲットボード: ZYBO (Z7-20)

事前知識

VivadoとXilinx SDKの簡単な使い方は分かっているものとします。下記記事を参考にしてください。

全体の流れ

まず、Vivado HLSでナベアツIPを作ります。Vivado HLSはあくまでIPを作るツールです。 そのため、Vivado HLSでIPを作った後に、Vivadoで全体のハードウェアを作ります。その中で先ほど作成したナベアツIPを使用します。Vivadoでハードウェア(hdf)を作り、エクスポートしたら、最後にXilinx SDKで制御プログラムを作成します。

事前準備

Vivado HLSでZYBOボードを参照できるようにします。C:\Xilinx\Vivado\2017.4\common\config\VivadoHls_boards.xmlに、以下2行を追加します。

VivadoHls_boards.xml
    <board name="Zybo_Z7_10" display_name="Zybo_Z7_10" family="zynq" part="xc7z010clg400-1"  device="xc7z010" package="clg400" speedgrade="-1" vendor="digilentinc.com" />
    <board name="Zybo_Z7_20" display_name="Zybo_Z7_20" family="zynq" part="xc7z020clg400-1"  device="xc7z020" package="clg400" speedgrade="-1" vendor="digilentinc.com" />

Vivado HLSでナベアツIPを作る

プロジェクトを作る

Vivado HSLを起動して、Create New Projectします。プロジェクト名はnabeatsuとします。ソースコードの追加などはひとまず何もせずにNextします。
最後に、ソリューションの設定をします。その時に、使用するデバイス設定が必要になりますので、今回はBoardsからZybo Z7 20 (先ほどの事前準備で追加した)を指定します。

01.jpg

C++ソースコードの実装と、Cシミュレーションの実行

作成するIPのソースコードを用意します。
メニューバー -> Project -> New Sourceと、New Test Benchで、ソースコードとテストベンチを作ります。今回はナベアツIPを作るので、それぞれ以下のようなファイルを作成します。

  • Source: nabeatsu.cpp
  • Test Bench: nabeatsu_tb.cpp

それぞれ、以下のように実装します。
アルゴリズムの説明は簡単なので省略しますが、以下がポイントになります。

  • 型として、ビット幅が指定できるap_uintを使用している
  • 出力ポートは、戻り値でもポインタ引数でもOK
  • テストベンチでちゃんとテストコードを書く。(なくても害はないが)
nabeatsu.cpp
#include <ap_int.h>

ap_uint<1> nabeatsu(ap_uint<32> number, ap_uint<1> *ahoOut)
{
    if (number % 3 == 0) {
        *ahoOut = 1;
        return 1;
    }
    while (number > 0) {
        if (number % 10 == 3) {
            *ahoOut = 1;
            return 1;
        }
        number /= 10;
    }

    *ahoOut = 0;
    return 0;
}
nabeatsu_tb.cpp
#include <ap_int.h>
#include <stdio.h>

extern ap_uint<1> nabeatsu(ap_uint<32> number, ap_uint<1> *ahoOut);

int main()
{
    printf("Hello \n");
    ap_uint<1> ahoOut;
    for (int i = 1; i < 100; i++) {
        printf("%d: ", i);
        if (nabeatsu(i, &ahoOut)) printf("aho");
        printf("\n");
    }
    return 0;
}

実装が完了したら、メニューバー -> Project -> Run C Simulationします。これによってテストベンチ内のmain関数が実行されて、Cレベルでの動作確認ができます。

C 高位合成する

とりあえず合成してみる

まず、メニューバー -> Project -> Project Settingsで、Synthesisを選んで、Top Functionnabeatsu関数を設定しておきます。
その後、メニューバー -> Solution -> Run C Synthesis -> Active Solutionで、高位合成を行います。
完了したら、高位合成結果のレポートが表示されます。ここで性能(レイテンシ等)や回路規模を確認します。

02.jpg

インターフェースをAXIにする

レポート内の、Interfaceを見ていただきたいのですが、これを見ると、number_VahoOut_Vの他にもap_startなどがあります。これらは、このIPをコントロールするためのインターフェイスになります。このIPの接続先が、PL内の他のIPだけであればこれでも良いのですが、今回はCPUから制御する予定です。そのため、CPUから制御しやすいようにAXIインターフェースに変更します。

nabeatsu.cppを開いた状態で、右側のペインでDirectiveタブを選びます。関数名や引数をダブルクリックすることで、どのようなインターフェースにするかを選択できます。なお、戻り値のインターフェースは関数名をダブルクリックして設定できます。インターフェイス仕様はこの記事の冒頭で記したとおりです。一点注意点は、ahoOutは直接LEDピン(M14)に接続します。そのため、余計な制御ピンは不要なので、ap_noneとします。しかし、それだけだとvalid以外のときに出力が不安定になってしまいます。そのため、オプションとしてregisterを指定します。これによって、出力状態が保たれます。

03.jpg

IP化する

Directive設定後、再度、Run C SynthesisしてRTLを出力します。その後、Export RTLをクリックして、IP化します。設定はデフォルトのままでOKです。

メモ: 最適化 / Directiveについて

今回はやりませんが、Directiveでは、上述したインターフェイスの他にも、ループをパイプライン化するとか展開するといった指定も可能です。回路規模や性能のトレードオフとなるところです。色々な設定を試して、レポートやRTLシミュレーション結果を見て、プロジェクトに最適な設定を探します。

このように、ソースコードは同じだけど、Directiveを色々変えて試したい、という状況が発生します。そのため、Vivado HLSではソリューションという概念を導入しています。ソースコードを共通としながら、ソリューション毎に別々のDirectiveを指定することが出来ます。これによって、後で比較したりするのが楽になります。

これも今回はやりませんが、RTLシミュレーションをやるには、Run C SynthesisでRTLを出力した後に、Run C/RTL Cosimulationします。シミュレーション設定ウィンドウが開くので、Dump Traceにportかallを設定して、OKします。完了後、Open Wave Viewerをクリックすることで、新規に開かれるVivadoウィンドウ上で信号波形を確認することができます。

Vivado (IP Integrator)で全体のハードウェア(hdf)を作る

プロジェクトを作る

Vivadoで、Zybo Z7-20ボードを指定して、新しいプロジェクトを作ります。IP Integratorで新規Block Designを作成して、PSを配置しておきます。(https://qiita.com/take-iwiw/items/24a8a94741fdbb80f62a )を参考にしてください。

ナベアツIPを組込む

Vivado HLSで作成したIPを取り込むために、IPレポジトリの追加を行う必要があります。
左側のFlow Navigator -> PROJECT MANAGER -> Settingsを開き、IP -> Repository から、先ほど作成IP化したナベアツIPの場所を指定します。IPのパスは、HSLプロジェクトのパス/高位合成したソリューション名/impl/ipになります。

04.jpg

再度Block Designに戻ると、Add IPからNabeatsuが指定できるようになっているはずです。これを追加して、Run Connection Automationで自動配線します。
配置されたnabeatsu_0ahoOut_V[0:0]で右クリック -> Make Externalをクリックして、外部ポートを作成しておきます。これは、後でLED(M14)に接続します。

05.jpg

IP間の接続が完了したら、Generate Output Products, Create HDL Wrapperします。

アホ出力をLEDに接続する

左側のFlow Navigator -> RTL ANALYSIS -> Open Elaborated Designを開きます。
下部に表示されるI/O Portsタブで、ahoOut_V_0[0]にM14 (LVCMOS33)を割り当てて、適当な名前でXDCファイルとして保存します。

ハードウェアを完成させる

Generate Bitstreamをクリックして、ビットストリームを作成します。その後、Export Hardwareでビットストリーム付きのhdfを作成します。

Xilinx SDKで制御プログラムを書く

VivadoのメニューバーからLaunch SDKでXilinx SDKを起動します。
standaloneプラットフォームのアプリケーションプロジェクトを作成します。
プログラム全体は以下のようになります。ナベアツIPへのアクセスは、自動生成されたbspプロジェクト内のxnabeatsu.hを使用します。

helloworld.c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xnabeatsu.h"

int main()
{
    init_platform();
    print("Hello World\n\r");

    XNabeatsu instNabe;
    u32 aho;
    XNabeatsu_Initialize(&instNabe, XPAR_NABEATSU_0_DEVICE_ID);

    for (int i = 1; i < 100; i++) {
        printf("%d: ", i);
        XNabeatsu_Set_number_V(&instNabe, i);
        XNabeatsu_Start(&instNabe);
        while (XNabeatsu_IsDone(&instNabe) == 0);
        aho = XNabeatsu_Get_return(&instNabe);
        if (aho == 1) {
            printf("aho\n");
        } else {
            printf("\n");
        }
        getchar();
    }

    cleanup_platform();
    return 0;
}

実行する

Program FPGAでビットストリームファイルを書き込んだ後に、ビルドしたCアプリケーションをRunします。
すると、入力数字が3の倍数か3を含むときだけアホになっていることが分かると思います。また、アホになっているときはLEDが点灯します。

実行結果
Hello World
1:
2:
3: aho
4:
5:
6: aho
7:
8:
9: aho
10:
11:
12: aho
13: aho
14:
15: aho
16:
17:
18: aho
19:
20:
21: aho
22:
23: aho

メモ: HLSで高位合成されたIPへのアクセス

自分でVerilogを書いた自作IPの場合は、レジスタの何番地が何用のパラメータ、といった情報を自分で分かっていると思います。HLSで高位合成されたIPでも、bsp内のxnabeatsu_hw.hに、アドレスと用途がまとまっています。しかし、処理開始/終了のためのstart/doneフラグなどを自分で管理するのは面倒です。

そのため、今回使用したように、自動生成されたライブラリ(xnabeatsu.c/h)を使用するのが楽です。

便利なことに、LinuxでUIOとしてアクセスするためのライブラリ(xnabeatsu_linux.c)も自動生成されます。しかもインターフェイス(xnabeatsu.h)は、共通で使用することが出来ます。そのため、IPをHLSで作った後に、まずはXSDKのstandaloneプラットフォームで軽く動作確認をして、その後、Linux側で本格的にアプリケーションで使用する、といったワークフローでも移行が簡単にできます。

参考資料

https://www.slideshare.net/marsee101/vivado-hls1
https://forums.xilinx.com/t5/Vivado-High-Level-Synthesis-HLS/Zybo-Board-Files-for-HLS/td-p/748198

11
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12