FC2カウンター FPGAの部屋 Vivado HLS

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

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

Vivado HLSでのAXI4-Stream のテンプレートを作成する2

Vivado HLSでのAXI4-Stream のテンプレートを作成する1”の続き。

前回は、Vivado HLSでAXI4-Stream のデータ幅を拡張するテンプレートを作成して合成してみたが、テンプレートのデータに配列を使ったので、合成できなかった。今回は、データを1次元配列にしてみよう。

(2018/02/12:書き換え)ブログを struct を使用した記述に書き換えました。@ciniml さん、教えて頂いてありがとうございました。

struct で 2 つ要素をまとめました。struct の中を本当は配列にしたかったのですが、配列だとAXI4-Stream でエラーになりました。修正したテンプレートの定義を含んだ stream_test.h を示す。

// stream_test.h
// 2018/02/11 by marsee
//

#ifndef __STREAM_TEST_H__
#define __STREAM_TEST_H__
#include <ap_fixed.h>

template<int W, int I, int U, int TI, int TD>
    struct ap_fixed1_axis{
        struct data {
            ap_fixed<W,I,AP_TRN,AP_WRAP> data0;
        } data;
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int W, int I, int U, int TI, int TD>
    struct ap_fixed2_axis{
        struct data {
            ap_fixed<W,I,AP_TRN,AP_WRAP> data0;
            ap_fixed<W,I,AP_TRN,AP_WRAP> data1;
        } data;
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int W, int I, int U, int TI, int TD>
    struct ap_fixed4_axis{
        struct data {
            ap_fixed<W,I,AP_TRN,AP_WRAP> data0;
            ap_fixed<W,I,AP_TRN,AP_WRAP> data1;
            ap_fixed<W,I,AP_TRN,AP_WRAP> data2;
            ap_fixed<W,I,AP_TRN,AP_WRAP> data3;
        } data;
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

#endif


修正した stream_test.cpp を示す。

// stream_test.cpp
// 2018/02/11 by marsee
//

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

#include "stream_test.h"

int stream_test(hls::stream<ap_fixed1_axis<16,6,1,1,1> >& ins,
        hls::stream<ap_fixed2_axis<16,6,1,1,1> >& outs){
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE s_axilite port=return
    ap_fixed1_axis<16,6,1,1,1> ins_t;
    ap_fixed2_axis<16,6,1,1,1> outs_t;

    for(int y=0; y<10; y++){
        for(int x=0; x<56; x++){
            ins >> ins_t;
            outs_t.data.data0 = ins_t.data.data0 * (ap_fixed<166, AP_TRN, AP_WRAP>)2.0;
            outs_t.data.data1 = ins_t.data.data0 * (ap_fixed<166, AP_TRN, AP_WRAP>)(-3.0);

            outs_t.user = 1;
            outs_t.last = 0;

            outs << outs_t;
        }
    }

    return(0);
}


今回、テストベンチを作成した。テストベンチの multi_test_tb.cpp を示す。

// stream_test_tb.cpp
// 2018/02/11 by marsee
//

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

#include "stream_test.h"

#define DATASIZE    560

int stream_test(hls::stream<ap_fixed1_axis<16,6,1,1,1> >& ins,
        hls::stream<ap_fixed2_axis<16,6,1,1,1> >& outs);

int main(){
    using namespace std;

    ap_fixed1_axis<16,6,1,1,1> in_ts;
    ap_fixed2_axis<16,6,1,1,1> out_ts;

    hls::stream<ap_fixed1_axis<16,6,1,1,1> > instream;
    hls::stream<ap_fixed2_axis<16,6,1,1,1> > outstream;

    for(int i=0; i<DATASIZE; i++){
        in_ts.data.data0 = i % 11;
        instream << in_ts;
    }

    stream_test(instream, outstream);

    for(int i=0; i<DATASIZE; i++){
        outstream >> out_ts;
        printf("i = %d, data1 = %f, data0 = %f\n", i, (float)out_ts.data.data1, (float)out_ts.data.data0);
    }

    return(0);
}


テストベンチができたので、シミュレーションをすることができる。
早速、C シミュレーションを行った。
stream_test_3_180212.png

2 つのデータが、うまく分離できているようだ。

さて、C コードの合成を行った。今度は成功。良かった。
stream_test_4_180212.png

FF と LUT も使われていて大丈夫そうだ。

C/RTL 協調シミュレーションを行った。
stream_test_6_180212.png

エラーで停止してしまった。エラー内容を示す。

F:/Xilinx/Vivado/2017.4/include/ap_stream.h:70:2: warning: #warning AP_STREAM macros are deprecated. Please use hls::stream<> from "hls_stream.h" instead. [-Wcpp]
apatb_stream_test.cpp: In function 'int AESL_WRAP_stream_test(hls::stream<ap_fixed1_axis<16, 6, 1, 1, 1> >&, hls::stream<ap_fixed2_axis<16, 6, 1, 1, 1> >&)':
apatb_stream_test.cpp:425:36: error: 'data' has no member named 'data1'
apatb_stream_test.cpp:478:36: error: 'data' has no member named 'data1'
apatb_stream_test.cpp:480:34: error: 'data' has no member named 'data1'
apatb_stream_test.cpp:2330:34: error: 'data' has no member named 'data1'
apatb_stream_test.cpp:2333:63: error: 'data' has no member named 'data1'
make: *** [obj/apatb_stream_test.o] Error 1
ERROR: [COSIM 212-317] C++ compile error.
ERROR: [COSIM 212-321] EXE file generate failed.
ERROR: [COSIM 212-321] EXE file generate failed.
ERROR: [COSIM 212-331] Aborting co-simulation: C simulation failed, compilation errors.
ERROR: [COSIM 212-4] *** C/RTL co-simulation finished: FAIL ***
command 'ap_source' returned error code
    while executing
"source C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO_Z7-20/test/stream_test/solution1/cosim.tcl"
    invoked from within
"hls::main C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO_Z7-20/test/stream_test/solution1/cosim.tcl"
    ("uplevel" body line 1)
    invoked from within
"uplevel 1 hls::main {*}$args"
    (procedure "hls_proc" line 5)
    invoked from within
"hls_proc $argv"
Finished C/RTL cosimulation

.
通常のAXI4-Stream のテンプレートを使っていなかったためか?でも、修正前は動作したのだが。。。

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
stream_test_5_180212.png

ちゃんと、FF も LUT もあるので、問題ないだろう?
  1. 2018年02月12日 05:16 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSでのAXI4-Stream のテンプレートを作成する1

これから作る畳み込みニューラルネットワークについての目標2”で構想したAXI4-Stream で接続する畳み込みニューラルネットワークを作成するためには、ストリームのデータ幅を拡張する必要がある。そのため、Vivado HLSでAXI4-Stream のデータ幅を拡張するテンプレートを作成してC コードの合成を行ってみよう。

Vivado HLS 2017.4 で stream_test プロジェクトを作成した。
stream_test_1_180211.png

stream_test.h を示す。

// stream_test.h
// 2018/02/11 by marsee
//

#ifndef __STREAM_TEST_H__
#define __STREAM_TEST_H__
#include <ap_fixed.h>

template<int W, int I, int N, int U, int TI, int TD>
    struct ap_fixed_axis{
        ap_fixed<W, I, AP_TRN, AP_WRAP> data[N];
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int W, int I, int N, int U, int TI, int TD>
    struct ap_ufixed_axis{
        ap_ufixed<W, I, AP_TRN, AP_WRAP> data[N];
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

#endif


データの配列 data[N] を定義して、畳み込みフィルタ数分のデータ幅のストリームを行うつもりだ。データの配列は任意精度固定小数点データ型とした。

stream_test.cpp を示す。

// stream_test.cpp
// 2018/02/11 by marsee
//

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

#include "stream_test.h"

int stream_test(hls::stream<ap_fixed_axis<16,6,2,1,1,1> >& ins,
        hls::stream<ap_fixed_axis<16,6,2,1,1,1> >& outs){
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE s_axilite port=return
    ap_fixed_axis<16,6,2,1,1,1> ins_t;
    ap_fixed_axis<16,6,2,1,1,1> outs_t;

    for(int y=0; y<10; y++){
        for(int x=0; x<56; x++){
            ins >> ins_t;
            outs_t.data[0] = ins_t.data[0] * (ap_fixed<166, AP_TRN, AP_WRAP>)2.0;
            outs_t.data[1] = ins_t.data[1] * (ap_fixed<166, AP_TRN, AP_WRAP>)3.0;

            outs_t.user = 1;
            outs_t.last = 0;

            outs << outs_t;
        }
    }

    return(0);
}



これで、C コードの合成を行ったところエラーが発生した。
stream_test_2_180211.png

ERROR: [XFORM 203-103] Cannot partition array 'ins.V.data.V' (stream_test/stream_test.cpp:12): different array partition directive on the same group of AXI-Stream ports.
ERROR: [HLS 200-70] Pre-synthesis failed.
command 'ap_source' returned error code
    while executing
"source C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO_Z7-20/test/stream_test/solution1/csynth.tcl"
    invoked from within
"hls::main C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO_Z7-20/test/stream_test/solution1/csynth.tcl"
    ("uplevel" body line 1)
    invoked from within
"uplevel 1 hls::main {*}$args"
    (procedure "hls_proc" line 5)
    invoked from within
"hls_proc $argv"
Finished C synthesis.


原因を検索してみると、Xilinx フォーラムの”AXI-Stream interface with data array side-channel does not build”が見つかった。
それによると配列じゃなくてフラットに 1 次元で書けということだ。

任意精度固定小数点データ型なので、どうやって 1 次元で書くかという問題はあるが、やり方はあると思う。次はそれを探っていこう。
  1. 2018年02月11日 08:57 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで関数内のBRAMを関数外から制御する2(C++のクラスを使って書いてみた)

Vivado HLSで関数内のBRAMを関数外から制御する1”の続き。

前回は、Vivado HLS 勉強会で一緒だった学生さんからVivado HLSの関数内で宣言したBRAM を外から読み書きしたいのだけど、どう書いたら良いか?という質問があったので、C 言語でサンプルコードを書いてみた。今回は、C++ のクラスを使って、サンプルコードを書いてみた。

まずは、C++ のクラスを使用して書いた、Vivado HLSの関数内で宣言したBRAM を外から読み書きするサンプルコードの bram_test2.cpp を示す。

// bram_test2.cpp
// 2018/02/06 by marsee
//

class bram {
    int array[1024];
public:
    void bram_write(int &index, int &data);
    void bram_read(int &index, int &data);
};

void bram::bram_write(int &index, int &data){
    array[index] = data;
}

void bram::bram_read(int &index, int &data){
    data = array[index];
}

int bram_test(int &index, int &wr, int &data){
#pragma HLS INTERFACE s_axilite port=data
#pragma HLS INTERFACE s_axilite port=wr
#pragma HLS INTERFACE s_axilite port=index
#pragma HLS INTERFACE s_axilite port=return

    static bram bram_inst;

    if(wr == 0){ // Read
        bram_inst.bram_read(index, data);
    }else// Write
        bram_inst.bram_write(index, data);
    }

    return(0);
}


なお、テストベンチは”Vivado HLSで関数内のBRAMを関数外から制御する1”の bram_test1_tb.cpp のままだ。

Vivado HLS 2017.4 で bram_test2 プロジェクトを作成した。
bram_test_15_180207.png

C シミュレーションを行った。結果を示す。前回同様だ。
bram_test_16_180207.png

C コードの合成を行った。結果を示す。
bram_test_17_180207.png

前回と比較すると LUT が 15 個多い。その他は同じだ。

C/RTL 協調シミュレーションを行ったが、前回同様に終了しない。強制的にストップした。

C/RTL 協調シミュレーションの波形を示す。やはり、WDATAなどが表示されない。
bram_test_18_180207.png

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
bram_test_19_180207.png

IP 化はできている。

ここで前回土曜に指示子をコメントアウトして、デフォルトのI/Oプロトコルとブロックレベルのプロトコルにしてみよう。
bram_test_20_180208.png

C コードの合成を行った。結果を示す。
bram_test_21_180208.png

このリソース使用量は前回と一致している。

C/RTL 協調シミュレーションを行った。結果を示す。
bram_test_22_180208.png

C/RTL 協調シミュレーションの波形を示す。
bram_test_23_180208.png

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
bram_test_24_180208.png

これも前回と同様のリソース使用量となった。
  1. 2018年02月10日 21:01 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで関数内のBRAMを関数外から制御する1

この前、Vivado HLS 勉強会で一緒だった学生さんからVivado HLSの関数内で宣言したBRAM を外から読み書きしたいのだけど、どう書いたら良いか?という質問があったので、サンプルコードを書いてみた。

bram_test1.cpp を示す。

// bram_test1.cpp
// 2018/02/06 by marsee
//

int bram_test(int &index, int &wr, int &data){
#pragma HLS INTERFACE s_axilite port=data
#pragma HLS INTERFACE s_axilite port=wr
#pragma HLS INTERFACE s_axilite port=index
#pragma HLS INTERFACE s_axilite port=return
    int array[1024];

    if(wr == 0){ // Read
        data = array[index];
    }else// Write
        array[index] = data;
    }

    return(0);
}


テストベンチの bram_test1_tb.cpp を示す。Read してWrite してRead というだけの単純なテストベンチだ。

// bram_test1_tb.cpp
// 2018/02/06 by marsee
//

#include <stdio.h>

int bram_test(int &index, int &wr, int &data);

int main(){
    int index, wr, data;

    wr = 0; index = 0;
    bram_test(index, wr, data);
    printf("data = %x\n", data);

    wr = 1; index = 0; data = 0x1;
    bram_test(index, wr, data);

    wr = 0; index = 0;
    bram_test(index, wr, data);
    printf("data = %x\n", data);

    wr = 0; index = 1;
    bram_test(index, wr, data);
    printf("data = %x\n", data);

    wr = 1; index = 1; data = 0x2;
    bram_test(index, wr, data);

    wr = 0; index = 1;
    bram_test(index, wr, data);
    printf("data = %x\n", data);

    return(0);
}


Vivado HLS 2017.4 で bram_test1 というプロジェクトを作成した。ターゲットはZYBO Z7-20 だ。
bram_test_1_180207.png

C シミュレーションを行った。配列のイニシャライズはされていないが、書き込んだ後は同じ値が読めている。
bram_test_2_180207.png

C コードの合成を行った。結果を示す。
bram_test_3_180207.png

BRAM_18K の使用量は 0 個だった。BRAM が実装されていない。
やはり、これは、C 言語では、関数内の宣言したローカル変数の配列はスタック領域にマップされて、関数から戻るときはクリアされる仕様が問題なんじゃないかな?ということで、static を配列の宣言に追加することにした。こうすれば、関数を抜けても配列は保持される。
bram_test_4_180207.png

// bram_test1.cpp
// 2018/02/06 by marsee
//

int bram_test(int &index, int &wr, int &data){
#pragma HLS INTERFACE s_axilite port=data
#pragma HLS INTERFACE s_axilite port=wr
#pragma HLS INTERFACE s_axilite port=index
#pragma HLS INTERFACE s_axilite port=return
    static int array[1024];

    if(wr == 0){ // Read
        data = array[index];
    }else// Write
        array[index] = data;
    }

    return(0);
}


これで、C シミュレーションを行った。結果を示す。
bram_test_5_180207.png

今度は配列がイニシャライズされていた。Read、Write も問題ない。

C コードの合成を行った。結果を示す。
bram_test_6_180207.png

BRAM_18K が 2 個実装されている。良さそうだ。

C/RTL 協調シミュレーションを行うと、終わらなかった。
なので、テストベンチをRead、Write 1 個のみにして、C/RTL 協調シミュレーションを行った。
bram_test_7_180207.png

でもやはり、終わらない。
bram_test_8_180207.png

途中で止めて波形を見てみよう。C/RTL 協調シミュレーションの波形を拡大してみてみよう。まずは、WVALID などのWrite の信号が表示されていない。これは、途中でC/RTL 協調シミュレーションを止めてしまったからなのか?
bram_test_14_180207.png

それじゃ、AXI4 Lite Slave インターフェースの予定だが、デフォルトでやってみよう。AXI4 Lite のインターフェースの指示子を取ってしまった。
bram_test_9_180207.png

これで、C コードの合成を行った。結果を示す。
bram_test_10_180207.png

FF、LUT は使用量が少なくなったが、BRAM_18K は 2 個使用されていて問題ないようだ。

C/RTL 協調シミュレーションを行った。結果を示す。
bram_test_11_180207.png

正常終了している。やはり、AXI4 Lite インターフェースの時だけおかしいのかな?

C/RTL 協調シミュレーションの波形を示す。
bram_test_12_180207.png

正常にアクセスできているようだ。

このまま、Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
bram_test_13_180207.png

BRAM も 2 個使われていて、問題なさそうだ。
  1. 2018年02月07日 05:06 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討9(畳み込み演算6)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討8(畳み込み演算5)”の続き。

前回は、2 つの畳み込み演算を並列に行うようにC ソースコードを書き換えたのだが、積和演算はLUT だけを使用した演算だった。さて、なぜLUT を使った演算にしてみたかというと、畳み込みニューラルネットワークを1クロックで演算できるようにするためにはDSP48E を使用すると足りなくなるという推測に基づいていた。もう 1 クロック動作はあきらめているので、DSP48E を使用しても問題は無さそうだ。ということで、DSP48E を使用する通常の積和演算をやってみた。

multi_test4.cpp で

#pragma HLS RESOURCE variable=out_temp core=AddSub

をコメントアウトした。
multi_test_66_180206.png

C コードの合成を行った。結果を示す。
multi_test_67_180206.png

Estimated は 10.68 ns で Target の 10 ns を超えてしまっているが、Latency は 2 クロックで、これは短い。
リソース使用量は、DSP48E を使用され、14 個、FF は 350 個、LUT は 1,304 個使用している。
Latency が 2 クロックは短いな。。。

C/RTL 協調シミュレーションを行った。結果を示す。
multi_test_68_180206.png

やはり Latency は 2 クロックだった。

C/RTL 協調シミュレーションの波形を示す。
multi_test_69_180206.png

2 つの畳み込み演算器がデータを受け取ってから、演算を行い、出力の値をその先の回路が受け取るまでに 2 クロックだった。

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
multi_test_70_180206.png

LUT は 392 個、FF が 179 個、何故か?DSP が 23 個だった?
インプリメント後の遅延時間は 9.404 ns でギリギリだが、Vivado で合成してもうまく行くかもしれない?

さて、Estimated は 10.68 ns なので、10 ns 以下にしようとして、Uncertainty を 3 ns にした。
その合成結果を示す。
multi_test_71_180206.png

Latency は 3 クロックに増加した。
リソース使用量は、DSP48E が 14 個で変わらなかった。FF は 600 個で 350 個から増えている。LUT は1,304 個で変わらなかった。

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
multi_test_72_180206.png

LUT は 578 個で、392 個から増えていた。FF は 256 個で、これも179 個から増えた。DSP48E は 14 個でこれは合成の時と同じ数だ。DSP48E は本来合成の時と同じ数のはずだ。なぜ、前は増えたのだろうか?
  1. 2018年02月06日 05:03 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討8(畳み込み演算5)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討7(畳み込み演算4)”の続き。

前回は、2 個目の畳み込み演算の重みの配列を使用して違いを確認した。今回は、2 つの畳み込み演算を並列にやってみよう。

2 つの畳み込み演算を並列に行うためにC ソースコードを少し変更した。プロジェクトも新しく作成した。
multi_test4 プロジェクトを示す。
multi_test_60_180205.png

multi_test4.cpp を示す。

// multi_test4.cpp
// 2018/02/04 by marsee
//

#include <ap_fixed.h>
#include "multi_test4.h"
#include "conv1_weight.h"

int multi_test4(ap_ufixed_in in[25], ap_fixed_add &out0, ap_fixed_add &out1){
#pragma HLS ARRAY_PARTITION variable=in complete dim=1

    ap_fixed_madd out_temp = 0.0;

#pragma HLS RESOURCE variable=out_temp core=AddSub
#pragma HLS PIPELINE II=1

    conv0: for(int k=0; k<2; k++){
        conv1: for(int m=0; m<5; m++){
            conv2: for(int n=0; n<5; n++){
                out_temp += in[m*5+n] * conv1_weight[k][0][m][n];
            }
        }
        if(k==0)
            out0 = out_temp;
        else
            out1 = out_temp;
        out_temp = 0.0;
    }

    return(0);
}


これは前と同じだが、multi_test4.h を示す。

// multi_test4.h
// 2018/02/04 by marsee
//

#ifndef __multi_test_H__
#define __multi_test_H__
#include <ap_fixed.h>

typedef ap_ufixed<80, AP_TRN, AP_WRAP> ap_ufixed_in;
typedef ap_fixed<91, AP_TRN, AP_WRAP> ap_fixed_weight;
typedef ap_fixed<226, AP_TRN, AP_WRAP> ap_fixed_madd;
typedef ap_fixed<166, AP_TRN_ZERO, AP_SAT> ap_fixed_add;

#endif


muti_test4_tb.cpp を示す。

// multi_test4_tb.h
// 2018/02/04 by marsee
//

#include "multi_test4.h"

int multi_test4(ap_ufixed_in in[25], ap_fixed_add &out0, ap_fixed_add &out1);

int main(void){
    ap_ufixed_in in[25];
    ap_fixed_add out0, out1;
    ap_ufixed_in v = 0.5;

    for(int i=0; i<25; i=i++){
        in[i] = (ap_ufixed_in)v;
        v += (ap_ufixed_in)0.00390625;
        printf("in[%d] = %f\n", i, (float)v);
    }

    multi_test4(in, out0, out1);

    printf("out0 = %f\n", (float)out0);
    printf("out1 = %f\n", (float)out1);

    return(0);
}


これで C シミュレーションを行った。結果を示す。
multi_test_61_180205.png

出力値は正常だ。

C コードの合成を行った。結果を示す。
multi_test_62_180205.png

Latency は 7 クロックだった。2 つの畳み込み演算を並列にできているようだ。
なお、conv0 の for ループに UNROLL 指示子を入れても、入れなくても結果は変わらなかった。
BRAM_18K や DSP48E は使用していない。FF は 1,424 個、LUT は 2,767 個使用している。

次に、C/RTL 協調シミュレーションを行った。結果を示す。
multi_test_63_180205.png

C/RTL 協調シミュレーションの波形を示す。
multi_test_64_180205.png

2 つの畳み込み演算器がデータを受け取ってから、演算を行い、出力の値をその先の回路が受け取るまでに 7 クロックだった。

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
multi_test_65_180205.png

こちらは、1 個目の畳み込み演算器の影響を受けるだろうから、この値になっていると思う。少し、危なさそうな数値だ。
  1. 2018年02月05日 05:13 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討7(畳み込み演算4)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討6(畳み込み演算3)”の続き。

前回は、従来通りの畳み込み演算の重みの配列を使用して、LUT にマップされた畳み込み演算を行うことができた。今回は、畳み込みフィルタは 2 個ある。前回はそのうちの 1 個目を使用していたので、今回は、2 個目の畳み込み演算の重みの配列を使用して違いを見てみよう。

まずは、2 個目の畳み込み演算の重みの配列を使用するために、19 行目の

conv1_weight[0][0][m][n]

conv1_weight[1][0][m][n]

に変更して、2 個目の畳み込み演算の重みの配列を使用する。
multi_test_54_180204.png

これで C シミュレーションを行った。結果を示す。
multi_test_55_180204.png

出力 out = -0.18555 だった。これだとマイナスの値なので、ReLU で 0 になるはずだ。

C コードの合成を行った。左に今回の 2 個目の畳み込み演算の重みの配列を使用した場合、右に前回の 1 個目の畳み込み演算の重みの配列を使用した場合の合成結果を示す。
multi_test_56_180204.pngmulti_test_50_180203.png

Latency は前回は 7 クロックだったが、今回は 4 クロックに減っている。Interval は変わらずに 1 クロックのままだ。
リソース使用量の FF も前回は、1,286 個だったが、今回は 478 個で約 37 % に減っている。LUT も前回は、2,106 個だったが、今回は805 個で、約 38 % に減っていた。
2 個目の畳み込み演算の重みの配列を見ると、0 が多く入っていた。そして他の重みもたぶんリソース使用量が少なくなる値だったんだと思う。
このように、DSP48E を使用せずに片方が定数の場合は演算を簡単化することができるのだが、重みの値によって圧縮率が異なる。2つに畳み込みフィルタを使用する場合に、2つ独立にインスタンスするとLatency が異なると結果が出てくるクロックが異なるので、Latency を合わせる必要がある。

次に、C/RTL 協調シミュレーションを行った。結果を示す。
multi_test_57_180204.png

やはり、Latency は 4 クロックだった。
C/RTL 協調シミュレーションの波形を示す。
multi_test_58_180204.png

畳み込み演算器がデータを受け取ってから、演算を行い、出力の値をその先の回路が受け取るまでに 4 クロックだった。

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
multi_test_59_180204.png

LUT は247 個、SRL は0 個なので、LUT は247 個使用する。こちらのFinal Timing は問題なさそうだ。
  1. 2018年02月04日 04:55 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0