FC2カウンター FPGAの部屋 2019年09月20日
FC2ブログ

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

FPGAの部屋

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

Vivado HLS 2019.1 でLOOG_MERGE 指示子を使う

Adom Taylor さんの”MicroZed Chronicles: HLS Working with Loops”を見て、LOOP_MERGE 指示子を発見した。Loop を 1 つにしてくれるということで、もしかしたら、AXI4-Stream で 2 つの入力ストリームがある時の 2 つの USER 待ちループを 1 個にしてくれるんじゃないだろうか?ということでやってみた。

今回、対象にするのは、
2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1
2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力2

2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1”の s_squares_axis.cpp ソースコードを見てもらうと分かると思うが、 x と y の 2 つの入力を一緒にしたコードを書いている。それを x だけ、y だけの USER 信号待ちループにして LOOP_MERGE 指示子を与えて、x と y の 2 つの入力を一緒にしたコードと同じ結果になるのではないか?ということでやってみよう。使用するのは、Vivado HLS 2019.1 とする。

s_squares_axis.cpp を示す。

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

int s_squares_axis(hls::stream<ap_axis<8,1,1,1> >& x,
    hls::stream<ap_axis<8,1,1,1> >& y, hls::stream<ap_axis<32,1,1,1> >& result){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=result
#pragma HLS INTERFACE axis register both port=y
#pragma HLS INTERFACE axis register both port=x
    ap_axis<8,1,1,1> xt;
    ap_axis<8,1,1,1> yt;
    ap_axis<32,1,1,1> rlt;

    xt.user = 0; yt.user = 0;
    Loop_xw : while(xt.user == 0){
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        x >> xt;
    }

    Loop_yw : while(yt.user == 0){
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        y >> yt;
    }

    Loop_main : for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        if(i != 0){
            x >> xt; y >> yt;
        }
        rlt.data = xt.data*xt.data + yt.data*yt.data;
        if(i == 0)
            rlt.user = 1;
        else
            rlt.user = 0;
        if(i==9)
            rlt.last = 1;
        else
            rlt.last = 0;
        result << rlt;
    }
    return(0);
}


x と y のUSER 待ちループを分離して、まずは、LOOP_MERGE 指示子を入れないで性能を見てみよう。

C シミュレーション結果を示す。
s_squares_axis_8_190920.png

問題ない。

C コードの合成結果を示す。
s_squares_axis_9_190920.png

Latency が 18 クロックで、”2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1”の 16 クロックよりも 2 クロック長い。

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

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

やはり、x と y の入力は排他的になっているようだ。

次に、Loop_xw と Loop_yw に LOOP_MERGE 指示子を入れた。s_squares_axis.cpp を示す。

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

int s_squares_axis(hls::stream<ap_axis<8,1,1,1> >& x,
    hls::stream<ap_axis<8,1,1,1> >& y, hls::stream<ap_axis<32,1,1,1> >& result){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=result
#pragma HLS INTERFACE axis register both port=y
#pragma HLS INTERFACE axis register both port=x
    ap_axis<8,1,1,1> xt;
    ap_axis<8,1,1,1> yt;
    ap_axis<32,1,1,1> rlt;

    xt.user = 0; yt.user = 0;
    Loop_xw : while(xt.user == 0){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        x >> xt;
    }

    Loop_yw : while(yt.user == 0){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        y >> yt;
    }

    Loop_main : for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        if(i != 0){
            x >> xt; y >> yt;
        }
        rlt.data = xt.data*xt.data + yt.data*yt.data;
        if(i == 0)
            rlt.user = 1;
        else
            rlt.user = 0;
        if(i==9)
            rlt.last = 1;
        else
            rlt.last = 0;
        result << rlt;
    }
    return(0);
}


これで、solution2 を作成してやってみた。

C コードの合成結果を示す。
s_squares_axis_12_190920.png

LOOP_MERGE 指示子を入れないときと同じ結果になった。

C/RTL 協調シミュレーションの結果を示す。
s_squares_axis_13_190920.png

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

これも LOOP_MERGE 指示子を入れないときと同じ結果だった。

次に、試しに Loop_main にも LOOP_MERGE 指示子を入れてみた。s_squares_axis.cpp を示す。

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

int s_squares_axis(hls::stream<ap_axis<8,1,1,1> >& x,
    hls::stream<ap_axis<8,1,1,1> >& y, hls::stream<ap_axis<32,1,1,1> >& result){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=result
#pragma HLS INTERFACE axis register both port=y
#pragma HLS INTERFACE axis register both port=x
    ap_axis<8,1,1,1> xt;
    ap_axis<8,1,1,1> yt;
    ap_axis<32,1,1,1> rlt;

    xt.user = 0; yt.user = 0;
    Loop_xw : while(xt.user == 0){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        x >> xt;
    }

    Loop_yw : while(yt.user == 0){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        y >> yt;
    }

    Loop_main : for(int i=0; i<10; i++){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
        if(i != 0){
            x >> xt; y >> yt;
        }
        rlt.data = xt.data*xt.data + yt.data*yt.data;
        if(i == 0)
            rlt.user = 1;
        else
            rlt.user = 0;
        if(i==9)
            rlt.last = 1;
        else
            rlt.last = 0;
        result << rlt;
    }
    return(0);
}


このコードでのC コードの合成結果、C/RTL 協調シミュレーション結果も同じだった。

LOOP_MERGE 指示子は入出力を伴うループのマージは行わないのかも知れない?
計算のループだけマージされるのかも知れない?
  1. 2019年09月20日 05:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0