FPGAの部屋

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。ご利用ください。 http://marsee101.web.fc2.com/index.html

2019年12月

今年 2019 年をざっと振り返ってみよう。

Ultra96のDisplay Port にカメラ画像を表示しようとしていた。”カメラ画像をDisplayPortに出力する9(アプリを作成、完成)”で完成した。

Ultra96のPMOD拡張ボードを作った。MIPI 拡張ボードのPMOD 拡張ボードを入れると 3 種類作成した。
Ultra96用PMOD拡張ボード12(改版した基板に部品を実装した)”これは、 2018 年 12 月だが、実際に使用したのは今年だった。
MIPIボード用PMOD拡張ボードをテストする1
Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板10(部品を実装)

Ultra96のMIPI拡張ボードを使ってDisplayPort に画像を出力しようとしたが、うまく行かなかった。残念。。。

Docker も使ってみた。Docker上でPetaLinux も動作させたが、アプリごとに環境を作っているとHDD がいくらあっても足りなそうなので、とりあえず Docker 使ってない。

Vivado HLS の OpenCV 対応も xfOpenCV になった。

DNN のFPGA 実装の Distillerhls4mlBinaryBrainkeras_compressor を試してみた。

Donkey Car も買ったな。。。Ultra96 を搭載するはずがまだやっていない。。。

SDxのプラットフォームを作ってみた。ベアメタル・アプリケーションはうまく行ったが、Linux アプリケーションはうまく行かなかった。

Vitisプラットフォーム、Vitis アプリケーション・プラットフォームを作った。現在進行系だ。

今年のイベントはなんと言っても私の定年退職だった。定年退職して、兼業がやりやすくなったので、HDLab さんでVivado HLS のセミナをやることにした。皆さんにお会いできるのが楽しみだ。
後は、四国しまなみ海道サイクリングだね。
ロードバイクも買っちゃたし、自転車のレースにも出た。来年も自転車に乗るぞ。。。

テンプレートで書いた畳み込みニューラルネットワークをRTLカーネルとしてVitisで実装する1(Vivado HLS 編 1)”の続き。

前回は、Vitis のRTL カーネルとして実装するためのC++ のソースコードテストベンチを貼った。今回は、そのコードをVivado HLS 2019.2 でプロジェクトを作成して実装していこう。

最初に all_layers_template プロジェクトを示す。
621b5f36.png


a917e9a3.png


このように層ごとにたくさんのファイルを入れてある。
なお、”Vitis のRTL カーネル”にあるように、Solution メニューから Solution Settings... を選択して、

config_interface -m_axi_addr64
config_sdx -target xocc

オプションを入れた。
27c80fbf.png


最初に C シミュレーションを行った。
b57de9ee.png

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../all_layers_template_axim.cpp in debug mode
   Compiling ../../../hw_ip/conv_layer1.cpp in debug mode
   Compiling ../../../hw_ip/input_layer.cpp in debug mode
   Compiling ../../../hw_ip/max_pooling.cpp in debug mode
   Compiling ../../../hw_ip/output_layer.cpp in debug mode
   Compiling ../../../hw_ip/relu_affine1.cpp in debug mode
   Compiling ../../../hw_ip/relu_conv1.cpp in debug mode
   Generating csim.exe
hw_error: i = 25 output = 2 t_test_num = 1
sw_error: i = 25 output_soft = 2 t_test_num = 1
dot2[0] = -5.00000000   dot2_soft[0] = -3.77501726
dot2[1] = 0.00000000    dot2_soft[1] = -0.13269189
dot2[2] = 0.00000000    dot2_soft[2] = 1.61074853

hw_error: i = 30 output = 2 t_test_num = 1
sw_error: i = 30 output_soft = 2 t_test_num = 1
dot2[0] = -6.00000000   dot2_soft[0] = -4.67336369
dot2[1] = 0.00000000    dot2_soft[1] = 0.12951475
dot2[2] = 0.00000000    dot2_soft[2] = 1.71587336

sw_error: i = 31 output_soft = 2 t_test_num = 1
dot2[0] = -7.00000000   dot2_soft[0] = -5.31440449
dot2[1] = 0.00000000    dot2_soft[1] = 0.69655895
dot2[2] = 0.00000000    dot2_soft[2] = 1.00723171

sw_error: i = 35 output_soft = 2 t_test_num = 1
dot2[0] = -7.00000000   dot2_soft[0] = -5.15462875
dot2[1] = 0.00000000    dot2_soft[1] = 0.19586089
dot2[2] = 0.00000000    dot2_soft[2] = 1.79063916

sw_error: i = 36 output_soft = 2 t_test_num = 1
dot2[0] = -7.00000000   dot2_soft[0] = -5.64889669
dot2[1] = 1.00000000    dot2_soft[1] = 0.69646239
dot2[2] = 0.00000000    dot2_soft[2] = 1.09402716

sw_error: i = 40 output_soft = 2 t_test_num = 1
dot2[0] = -7.00000000   dot2_soft[0] = -5.31394196
dot2[1] = 0.00000000    dot2_soft[1] = 0.30034199
dot2[2] = 0.00000000    dot2_soft[2] = 1.52586949

sw_error: i = 41 output_soft = 2 t_test_num = 1
dot2[0] = -8.00000000   dot2_soft[0] = -5.94443941
dot2[1] = 0.00000000    dot2_soft[1] = 0.61903512
dot2[2] = 0.00000000    dot2_soft[2] = 1.28180122

sw_error: i = 42 output_soft = 2 t_test_num = 1
dot2[0] = -10.00000000  dot2_soft[0] = -7.44187164
dot2[1] = 1.00000000    dot2_soft[1] = 1.10615981
dot2[2] = 0.00000000    dot2_soft[2] = 1.35738707

sw_error: i = 45 output_soft = 2 t_test_num = 1
dot2[0] = -8.00000000   dot2_soft[0] = -5.92508411
dot2[1] = 0.00000000    dot2_soft[1] = 0.44851223
dot2[2] = 0.00000000    dot2_soft[2] = 1.43742454

sw_error: i = 46 output_soft = 2 t_test_num = 1
dot2[0] = -10.00000000  dot2_soft[0] = -7.76649952
dot2[1] = 1.00000000    dot2_soft[1] = 0.82863915
dot2[2] = 0.00000000    dot2_soft[2] = 1.88942850

sw_error: i = 47 output_soft = 2 t_test_num = 1
dot2[0] = -12.00000000  dot2_soft[0] = -9.50911713
dot2[1] = 1.00000000    dot2_soft[1] = 1.48399019
dot2[2] = 0.00000000    dot2_soft[2] = 1.85759318

hw_error: i = 75 output = 2 t_test_num = 1
sw_error: i = 75 output_soft = 2 t_test_num = 1
dot2[0] = -5.00000000   dot2_soft[0] = -4.04238653
dot2[1] = -1.00000000   dot2_soft[1] = -1.22402656
dot2[2] = 2.00000000    dot2_soft[2] = 3.36929369

hw_error: i = 76 output = 2 t_test_num = 1
sw_error: i = 76 output_soft = 2 t_test_num = 1
dot2[0] = -6.00000000   dot2_soft[0] = -4.09871578
dot2[1] = 0.00000000    dot2_soft[1] = -0.46985394
dot2[2] = 0.00000000    dot2_soft[2] = 1.61257589

hw_error: i = 80 output = 2 t_test_num = 1
sw_error: i = 80 output_soft = 2 t_test_num = 1
dot2[0] = -6.00000000   dot2_soft[0] = -4.33292818
dot2[1] = 0.00000000    dot2_soft[1] = -0.96692348
dot2[2] = 1.00000000    dot2_soft[2] = 2.98383069

hw_error: i = 81 output = 2 t_test_num = 1
sw_error: i = 81 output_soft = 2 t_test_num = 1
dot2[0] = -6.00000000   dot2_soft[0] = -4.40864801
dot2[1] = 0.00000000    dot2_soft[1] = -0.15780880
dot2[2] = 0.00000000    dot2_soft[2] = 1.26864278

hw_error: i = 85 output = 2 t_test_num = 1
sw_error: i = 85 output_soft = 2 t_test_num = 1
dot2[0] = -6.00000000   dot2_soft[0] = -4.16326904
dot2[1] = 0.00000000    dot2_soft[1] = -0.84592772
dot2[2] = 1.00000000    dot2_soft[2] = 2.42255425

sw_error: i = 86 output_soft = 2 t_test_num = 1
dot2[0] = -6.00000000   dot2_soft[0] = -4.36515617
dot2[1] = 0.00000000    dot2_soft[1] = -0.08813666
dot2[2] = 0.00000000    dot2_soft[2] = 0.97706115

hw_error: i = 90 output = 2 t_test_num = 1
sw_error: i = 90 output_soft = 2 t_test_num = 1
dot2[0] = -5.00000000   dot2_soft[0] = -4.02276182
dot2[1] = 0.00000000    dot2_soft[1] = -0.66237617
dot2[2] = 0.00000000    dot2_soft[2] = 1.72938108

sw_error: i = 91 output_soft = 2 t_test_num = 1
dot2[0] = -5.00000000   dot2_soft[0] = -3.85103607
dot2[1] = 0.00000000    dot2_soft[1] = -0.09844255
dot2[2] = 0.00000000    dot2_soft[2] = 0.42963967

sw_error: i = 95 output_soft = 2 t_test_num = 1
dot2[0] = -6.00000000   dot2_soft[0] = -4.07760668
dot2[1] = 0.00000000    dot2_soft[1] = -0.30057180
dot2[2] = 0.00000000    dot2_soft[2] = 0.90393031

hw_err_cnt = 8 sw_err_cnt = 20
WARNING: Hls::stream 'hls::stream<ap_axiu<32, 1, 1, 1> >.1' contains leftover data, which may result in RTL simulation hanging.
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


前にやった結果と同じなので、問題はないだろう。ちなみに 300 個やってハードウェアのエラーが 8 個、ソフトウェアのエラーが 20 個で、ハードウェアの正解確率は約 97 % 、ソフトウェアの正解確率は約 93 % だった。

次に、 C コードの合成を行った。
c1cd278c.png


レイテンシは max で 9176 クロックで 200 MHz なので、約 46 us かかっている。約 22,000 fps となる。

Export RTL を行った。Vivado synthesis, place and route にチェックを入れた。
RTL_kernel_13_191228.png

FF は 11.915 個だったが、合成時には、 15,866 個だったので、小さくなった。
LUT は 9,793 個とSRL の 550 個を足すと 10,343 個だったが、合成時には、 16, 774 個だった。

CP achieved post-implementation は 4.336 ns となっているので、問題はないだろう。

ip ディレクトリが作られ、kernel ディレクトリの下に kernel.xml が生成された。
6bc2209c.png


kernel.xml を示す。
77907cdb.png


all_layers_dnn.xo が生成されている。
6bc2209c.png

以前テンプレートを使用して、パラメータを変更できる形に書いた畳み込みニューラルネットワークをVivado HLS 2019.2 でIP にすることにした。Vitis のアクセラレーション・プラットフォームでやってみたいためだ。実はこのテンプレートを使用して、パラメータを変更できる形に書いた畳み込みニューラルネットワークをVitis のアクセラレーション・プロジェクトとして作成したところ、ビルドが通らなかったので、Vivado HLS でIP 化した後で、Vitis のRTL カーネルとして使ってみたいということだ。
Vivado HLS でIP 化すると、チューニングもしやすいし、C/RTL 協調シミュレーションで波形も確認できるのでとても良い。そこで、IP 化したVerilog HDL コードをRTL カーネルとしてVitis で使いたいと考えたわけだ。。。
今回は、Vitis のRTL カーネルと実装するためのC++ のソースコードテストベンチを貼っておく。

テンプレートで書いた CNN の記事を下に示す。
これはテンプレートで書いた各層をHLSストリームで接続するフレームワークになっている。テンプレートのパラメータを設定すれば、いろいろな CNN として使用することができるように設計されている(はず)。現在は、白線走行用の CNN のパラメータがテンプレートに設定されている。
今回は、入力層ー畳み込み層ーReluーMaxpoolingー全結合層ーReluー全結合層ー出力層だが、途中のHLSストリームの幅を合わせれば、何層でも接続することができる。ただし、余り多いとVivado HLSで合成できなくなる。
その場合は、HLSストリームで接続なので、途中で切って合成し、IP インテグレータでつなぐこともできる。
テンプレートで書いた畳み込 みニューラルネットワーク1(ソースコード)
テンプレートで書いた畳み込 みニューラルネットワーク2(C シミュレーションとC コードの合成)
テンプレートで書いた畳み込 みニューラルネットワーク2(C/RTL協調シミュレーションとExport RTL)

このソースコードを、”Vitis のRTL カーネル”に沿って書き換えた。

ソースコードの all_layers_template_axim.cpp を示す。

// all_layers_template_axim.cpp
// 2018/05/10 by marsee
// 2019/12/28: VitisのRTLカーネルととして使用するためにall_layers_dnnを追加
//

#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include <hls_video.h>
#include <stdint.h>

#include "layer_general.h"
#include "all_layers_template.h"

int input_layer(hls::stream<ap_axiu<32,1,1,1> >&ins,
    hls::stream<ap_fixed_axis<9,1,1,1> >&outs);

int conv_layer1(hls::stream<ap_fixed_axis<9,1,1,1> >& ins,
    hls::stream<ap_fixed_axis<16,6,2,1> >& outs);

int relu_conv1(hls::stream<ap_fixed_axis<16,6,2,1> >& ins,
    hls::stream<ap_fixed_axis<16,6,2,1> >& outs);

int max_pooling(hls::stream<ap_fixed_axis<16,6,2,1> >& ins,
    hls::stream<ap_fixed_axis<16,6,2,1> >& outs);

int affine_layer1(hls::stream<ap_fixed_axis<16,6,2,1> >& ins,
    hls::stream<ap_fixed_axis<19,7,1,1> >& outs);

int relu_affine1(hls::stream<ap_fixed_axis<19,7,1,1> >& ins,
    hls::stream<ap_fixed_axis<19,7,1,1> >& outs);

int affine_layer2(hls::stream<ap_fixed_axis<19,7,1,1> >& ins,
    hls::stream<ap_fixed_axis<12,7,1,1> >& outs);

int output_layer(hls::stream<ap_fixed_axis<12,7,1,1> >& ins, output_type& output,
    out_affine_type dot2[NUMBER_OF_OUTPUT_LAYER]);

int all_layers(hls::stream<ap_axiu<32,1,1,1> >& ins, output_type& output,
    out_affine_type dot2[NUMBER_OF_OUTPUT_LAYER]){
//#pragma HLS INTERFACE s_axilite port=output
//#pragma HLS INTERFACE s_axilite port=dot2
//#pragma HLS ARRAY_PARTITION variable=dot2 complete dim=1
//#pragma HLS INTERFACE s_axilite port=return
//#pragma HLS INTERFACE axis register both port=ins
#pragma HLS DATAFLOW

    hls::stream<ap_fixed_axis<9,1,1,1> > outs_input_layer;
//#pragma HLS STREAM variable=outs_input_layer depth=560 dim=1
    hls::stream<ap_fixed_axis<16,6,2,1> > outs_conv_layer;
//#pragma HLS STREAM variable=outs_conv_layer depth=312 dim=1
    hls::stream<ap_fixed_axis<16,6,2,1> > outs_relu_conv1;
//#pragma HLS STREAM variable=outs_relu depth=312 dim=1
    hls::stream<ap_fixed_axis<16,6,2,1> > outs_max_pooling;
//#pragma HLS STREAM variable=outs_max_pooling depth=78 dim=1
    hls::stream<ap_fixed_axis<19,7,1,1> > outs_affine_layer1;
//#pragma HLS STREAM variable=outs_affine_layer1 depth=100 dim=1
    hls::stream<ap_fixed_axis<19,7,1,1> > outs_relu_affine1;
//#pragma HLS STREAM variable=outs_relu_affine1 depth=100 dim=1
    hls::stream<ap_fixed_axis<12,7,1,1> > outs_affine_layer2;
//#pragma HLS STREAM variable=outs_affine_layer2 depth=3 dim=1

    input_layer(ins, outs_input_layer);
    conv_layer1(outs_input_layer, outs_conv_layer);
    relu_conv1(outs_conv_layer, outs_relu_conv1);
    max_pooling(outs_relu_conv1, outs_max_pooling);
    affine_layer1(outs_max_pooling, outs_affine_layer1);
    relu_affine1(outs_affine_layer1, outs_relu_affine1);
    affine_layer2(outs_relu_affine1, outs_affine_layer2);
    output_layer(outs_affine_layer2, output, dot2);

    return(0);
}

extern "C" {
void all_layers_dnn(volatile uint32_t *inm, volatile uint32_t *output,
        volatile int32_t *dot2, int32_t x_size, int32_t y_size){
#pragma HLS INTERFACE m_axi port=inm offset = slave bundle = gmem
#pragma HLS INTERFACE m_axi port=output offset = slave bundle = gmem
#pragma HLS INTERFACE m_axi port=dot2 offset = slave bundle = gmem2
#pragma HLS INTERFACE s_axilite port = x_size bundle = control
#pragma HLS INTERFACE s_axilite port = y_size bundle = control
#pragma HLS INTERFACE s_axilite port = return bundle = control

#pragma HLS DATAFLOW
    hls::stream<ap_axiu<32, 1, 1, 1> > ins;
    ap_axiu<32,1,1,1> element;
    out_affine_type dot2_o[NUMBER_OF_OUTPUT_LAYER];
    output_type output_o;

    Loop_y: for(int y=0; y<y_size; y++){
#pragma HLS LOOP_TRIPCOUNT min=10 max=10 avg=10
        Loop_x: for(int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=56 max=56 avg=56
#pragma HLS PIPELINE II=1
            element.data = ap_uint<32>(inm[x_size*y+x]);
            if(x==0 && y==0) // 最初のデータ
                element.user = 1;
            else
                element.user = 0;
            if(x==(x_size-1)) // 行の終了
                element.last = 1;
            else
                element.last = 0;

            ins << element;
        }
    }
    all_layers(ins, output_o, dot2_o);

    *output = uint32_t(output_o);

    Loop_dot2: for(int i=0; i<NUMBER_OF_OUTPUT_LAYER; i++){
#pragma HLS PIPELINE II=1
        dot2[i] = int32_t(dot2_o[i]);
    }
}
}


テストベンチの all_layers_template_axim_tb.cpp を示す。

// all_layers_template_axim_tb.cpp
// 2018/05/12 by marsee
// 2019/12/28: VitisのRTLカーネルととして使用するためにall_layers_dnnを追加
//

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <math.h>
#include <ap_axi_sdata.h>
#include <hls_video.h>
#include <vector>
#include <stdint.h>

#include "layer_general.h"
#include "all_layers_template.h"

#include "curve_data_0_100.h"
//#include "curve_data_2500_2600.h"
//#include "curve_data_5000_5100.h"

#define ALL_DATA_NUM   300
#define NUM_OF_KERNELS 2
#define COULMN_PIXELS 56
#define ROW_PIXELS 10
#define ALL_PIXELS 560
#define NUM_OF_OUTPUT 3

#define NUM_ITERATIONS  300 // C Simulation
//#define NUM_ITERATIONS    2 // C/RTL CoSimulation

void all_layers_dnn(volatile uint32_t *inm, volatile uint32_t *output,
        volatile int32_t *dot2, int32_t x_size, int32_t y_size);

int all_layers_soft(hls::stream<ap_axiu<32,1,1,1> >& ins, output_type& output,
        float dot2[NUMBER_OF_OUTPUT_LAYER]);

int main(){
    using namespace std;

    hls::stream<ap_axiu<32,1,1,1> > ins;
    hls::stream<ap_axiu<32,1,1,1> > ins_soft;
    output_type output_soft;
    uint32_t output;
    float dot2_soft[NUMBER_OF_OUTPUT_LAYER];
    ap_axiu<32,1,1,1> pix;
    int hw_err_cnt = 0;
    int sw_err_cnt = 0;

    vector<uint32_t> pixel(ROW_PIXELS * COULMN_PIXELS);
    vector<int32_t> dot2(NUMBER_OF_OUTPUT_LAYER);

    for(int i=0; i<NUM_ITERATIONS; i++){
        // ins に入力データを用意する
        for(int y=0; y<ROW_PIXELS; y++){
            for(int x=0; x<COULMN_PIXELS; x++){
                // 1 画面分のデータを ins、ins_soft に入力する
                pix.data = ap_uint<32>(t_train_256[i][y*COULMN_PIXELS+x]);
                pixel[y*COULMN_PIXELS+x] = uint32_t(t_train_256[i][y*COULMN_PIXELS+x]);

                if (x==0 && y==0)    // 最初のデータの時に TUSER を 1 にする
                    pix.user = 1;
                else
                    pix.user = 0;

                if (x == COULMN_PIXELS-1) // 行の最後でTLASTをアサートする
                    pix.last = 1;
                else
                    pix.last = 0;

                ins << pix;
                ins_soft << pix;
            }
        }

        all_layers_dnn(pixel.data(), &output, dot2.data(), COULMN_PIXELS, ROW_PIXELS);
        all_layers_soft(ins_soft, output_soft, dot2_soft);

        int t_test_num = 0;
        for(int m=0; m<NUMBER_OF_OUTPUT_LAYER; m++){
            if(t_test[i][m] == 1.0f){
                t_test_num = m;
                break;
            }
        }
        // out と out_soft を比較する
        /* cout << "output" << " = " << int(output) << " output_soft = " << int(output_soft) << endl;
        for(int j=0; j<NUMBER_OF_OUTPUT_LAYER; j++){
            cout << "dot2[" << j << "] = " << float(dot2[j]) << " dot2_soft[" << j << "] = " << dot2_soft[j] << endl;
        } */
        if(int(output) != t_test_num){
            cout << "hw_error: i = " << i << " output = " << int(output) << " t_test_num = " << t_test_num << endl;
            hw_err_cnt++;
            //return(1);
        }
        if(int(output_soft) != t_test_num){
            cout << "sw_error: i = "<< i << " output_soft = " << int(output_soft) << " t_test_num" " = " << t_test_num << endl;
            sw_err_cnt++;
            //return(1);
        }
        if(int(output) != t_test_num || int(output_soft) != t_test_num){
            for(int j=0; j<NUMBER_OF_OUTPUT_LAYER; j++){
                cout << "dot2[" << j << "] = " << fixed << setprecision(8) << float(dot2[j]) << "   dot2_soft[" << j << "] = " << dot2_soft[j] << endl;
            }
            cout << endl;
        }
    }
    cout << "hw_err_cnt = " << hw_err_cnt << " sw_err_cnt = " << sw_err_cnt << endl;

    return(0);
}


今のところ、私はほとんどVivado HLS をGUI で操作しているが、TCL スクリプトで動作させることもできる。
GUI でやった履歴を TCL スクリプトにする機能が Vivado HLS にはある。
それは、 solution? -> script.tcl だ。
aecfdfe5.png


script.tcl は solution? ディレクトリの下にある。
5ad9f7ab.png


lap_filter_axis_dma.cpp を合成して、Export RTL した時の TCL スクリプトを貼っておく。

############################################################
## This file is generated automatically by Vivado HLS.
## Please DO NOT edit it.
## Copyright (C) 1986-2019 Xilinx, Inc. All Rights Reserved.
############################################################
open_project lap_filter_axis_dma
set_top lap_filter_axis_dma
add_files lap_filter_axis_dma/lap_filter_axis_dma.cpp
open_solution "solution1"
set_part {xczu3cg-sbva484-1-e}
create_clock -period 10 -name default
config_export -format ip_catalog -rtl verilog -vivado_optimization_level 0 -vivado_phys_opt none -vivado_report_level 0 -xo /home/masaaki/Vivado_HLS/Ultra96/test/lap_filter_axis_dma.xo
config_interface   -m_axi_addr64 -m_axi_offset off -register_io off -trim_dangling_port=0
config_sdx -target xocc
config_compile -name_max_length 80 -pipeline_loops 64
config_schedule -enable_dsp_full_reg
#source "./lap_filter_axis_dma/solution1/directives.tcl"
#csim_design
csynth_design
#cosim_design
export_design -rtl verilog -format ip_catalog -xo /home/masaaki/Vivado_HLS/Ultra96/test/lap_filter_axis_dma.xo


この TCL スクリプトを実行する時は、
vivado_hls -f script.tcl
とする。

今まで、Vitis のアクセラレーション・プラットフォームのC/C++ カーネルを実装してきたが、RTL カーネルもやってみようと思う。RTL カーネルの作成条件について見ていこう。
なお、C/C++ カーネルを作っていると、性能やクリティカルパスなどの情報が無いかもしくは、分かりにくい。そこで、Vivado HLS でC/C++ コードを合成して RTLカーネルを作成して、それをVitis で使っても良いのでは?と思っている。

Vitis Unified Software Development Platform Documentation
Vitis Application Acceleration Development Flow DocumentationDeveloping ApplicationsRTL Kernels を見ていこう。

Vitisコア開発キットの各ハードウェアカーネルは、Xilinxオブジェクト(.xo)ファイルに個別にコンパイルされるそうだ。 .xo ファイルはVivado HLS 2019.2 で出力することができる。 .xo ファイルは、FPGA実行可能ファイル(xclbin)にリンクするためのアプリケーションプロジェクトに結合できるそうだ。

RTL Kernels の Table 1. RTL Kernel Interface and Port Requirements をChrome で翻訳したものを引用する。

表1. RTLカーネルインターフェイスとポートの要件ポートまたはインターフェース
7585719c.png


結局、Vitis のC/C++ カーネルを書く制約とほとんど一緒で、AXI4 Master を 64 ビット・アドレスにすれば良いということみたいだ。
Vivado HLS でAXI4 Master を 64 ビット・アドレスにするには、

config_interface -m_axi_addr64

オプションを入れれば良い。
GUI でやるには、Solution メニューから Solution Settings... を選択する。
左のペインのGeneral で、config_interface が無い場合は、Add... ボタンをクリックして、ある場合はEdit... ボタンをクリックして、
m_axi_addr64 のチェックボックスをチェックする。
93c9a73e.png


Table 2. Address Map、Table 3. Control (0x0)、Table 5. IP Interrupt Enable (0x8)、Table 6. IP Interrupt Status (0xC)はVivado HLS のVerilog HDL 出力のAXI4 Lite のレジスタマップそのままだ。
lap_filter_axis_dma.cpp のAXI4 Lite Slave のマップを示す。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of inm
//        bit 31~0 - inm[31:0] (Read/Write)
// 0x14 : Data signal of inm
//        bit 31~0 - inm[63:32] (Read/Write)
// 0x18 : reserved
// 0x1c : Data signal of outm
//        bit 31~0 - outm[31:0] (Read/Write)
// 0x20 : Data signal of outm
//        bit 31~0 - outm[63:32] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of x_size
//        bit 31~0 - x_size[31:0] (Read/Write)
// 0x2c : reserved
// 0x30 : Data signal of y_size
//        bit 31~0 - y_size[31:0] (Read/Write)
// 0x34 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


Packaging the RTL Code as Vivado IP はVivado HLS でIP 化するならば問題ないはず。

Creating the Kernel Description XML File では kernel.xml を生成する必要がある。
Vivado HLS で kernel.xml を生成するためには、

config_sdx -target xocc

オプションを入れれば良い。
GUI でやるには、Solution メニューから Solution Settings... を選択する。
左のペインのGeneral で、 config_sdx が無い場合は、Add... ボタンをクリックして、ある場合はEdit... ボタンをクリックして、Parameters の target を選択して、xocc に変更する。
b443634d.png


これで、C コードの合成とExport RTL を行うと solution1/impl/kernel ディレクトリの下に、kernel.xml が生成された。
7a87ea8f.png


ただし、トップの関数にはVitis のC/C++ カーネルを作成するための制約と同じルールを適用する必要があるようだ。
トップ関数の返り値は void で、関数全体を extern "C" { } で囲う必要がある。
efb0b3fa.png


lap_filter_axis_dma.cpp を Vivado HLS 2019.2 で合成し、Export RTL した時の kernel.xml を示す。
d70f7d75.png

↑このページのトップヘ