FC2カウンター FPGAの部屋 2018年08月08日
FC2ブログ

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

FPGAの部屋

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

2つのHLSストリームを同時に入力して演算してHLSストリーム出力

2つのHLSストリームを同時に入力して演算してHLSストリーム出力したい。これは、2つの畳み込み演算を1つにまとめる機能を持ったIPをVivado HLSで作るために必要となる。
ただし、C やC++ は並列動作が想定されていないので、どうなるか分からない。

まずは、ソースファイルの two_hls_streams.cpp を示す。

// two_hls_streams.cpp
// 2018/08/07 by marsee
//

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

int two_hls_streams(hls::stream<ap_axis<32,1,1,1> >& ins0,
    hls::stream<ap_axis<32,1,1,1> >& ins1,
    hls::stream<ap_axis<32,1,1,1> >& outs){
    ap_axis<32,1,1,1> in_val0;
    ap_axis<32,1,1,1> in_val1;
    ap_axis<32,1,1,1> out_val;

    Loop1 : do {    // user が 1になった時にスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins0 >> in_val0;
    } while(in_val0.user == 0);

    Loop2 : do {    // user が 1になった時にスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins1 >> in_val1;
    } while(in_val1.user == 0);

    Loop3 : for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        if(i != 0){
            ins0 >> in_val0;
            ins1 >> in_val1;
        }
        out_val.data = in_val0.data * in_val1.data;
        out_val.user = in_val0.user;
        out_val.last = in_val0.last;
        outs << out_val;
    }
    return(0);
}


ins0 のスタートを待った後に、ins1 のスタートを待って、ins0 と ins1 を乗算して、outs にHLSストリーム出力している。
次に、テストベンチの two_hls_streams_tb.cpp を示す。

// two_hls_streams_tb.cpp
// 2018/08/07 by marsee
//

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

int two_hls_streams(hls::stream<ap_axis<32,1,1,1> >& ins0,
    hls::stream<ap_axis<32,1,1,1> >& ins1,
    hls::stream<ap_axis<32,1,1,1> >& outs);

int main(){
    hls::stream<ap_axis<32,1,1,1> > ins0;
    hls::stream<ap_axis<32,1,1,1> > ins1;
    hls::stream<ap_axis<32,1,1,1> > outs;
    ap_axis<32,1,1,1> in_val0;
    ap_axis<32,1,1,1> in_val1;
    ap_axis<32,1,1,1> out_val;

    for(int i=0; i<2; i++){ // dummy
        in_val0.data = i;
        in_val0.user = 0;
        in_val0.last = 0;
        ins0 << in_val0;
    }

    for(int i=0; i<3; i++){ // dummy
        in_val1.data = i;
        in_val1.user = 0;
        in_val1.last = 0;
        ins1 << in_val1;
    }

    for(int i=0; i<10; i++){
        in_val0.data = i;
        in_val1.data = i+1;
        if(i == 0){
            in_val0.user = 1;
            in_val1.user = 1;
        }else{
            in_val0.user = 0;
            in_val1.user = 0;
        }
        if(i == 9){
            in_val0.last = 1;
            in_val1.last = 1;
        }else{
            in_val0.last = 0;
            in_val1.last = 0;
        }
        ins0 << in_val0;
        ins1 << in_val1;
    }

    two_hls_streams(ins0, ins1, outs);

    for(int i=0; i<10; i++){
        outs >> out_val;
        std::cout << "outs[" << i << "].data = " << out_val.data
                << " .user = " << out_val.user << " .last = " << out_val.last
                << std::endl;
    }

    return(0);
}


Vivado HLS 2018.2 の two_hls_streams プロジェクトを作成した。
two_hls_streams_1_180808.png

C シミュレーションを行った。
two_hls_streams_2_180808.png

C コードの合成を行った。
two_hls_streams_3_180808.png

Estimated は8.510 ns でLatency は 16 クロックだった。

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

Latency は 21 クロックだった。

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

ins0 のスタートを検出したら ins1 のスタートを検出している。つまり、シリアライズされているので、ここを並列動作させた方が良かな?
とりあえずは、演算本体部分はins0 と ins1 を同時に読んで演算を行っているようだ。
  1. 2018年08月08日 05:21 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0