FC2カウンター FPGAの部屋 Vivado HLS 2014.4 でサイドチャネル付き AXI4-Stream をテストする1(C++ソースコードの公開)
FC2ブログ

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

FPGAの部屋

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

Vivado HLS 2014.4 でサイドチャネル付き AXI4-Stream をテストする1(C++ソースコードの公開)

Vivado HLS 2014.4 で AXI4-Stream をテストする4(1次元のフィルタのテスト3、RTLシミュレーション)”の続き。

今回は、サイドチャネル付きの AXI4-Stream をテストしてみよう。AXI4-Stream のサイドチャネルとはなにか?と言うと、AXI4-Stream には、TKEEP, TSTRB, TUSER, TLALST, TID, TDEST などの信号が定義されている。(”AXI4-Stream のお勉強”を参照のこと)
これを使用して、画像のフレーム同期を取ってみよう。AXI VDMAでは TUSER が MM2S, S2MM とも start-of-frame としてドライブされている。更に TLAST もラインの終わりにアサートされている。(”AXI Video Direct Memory Access v6.2 LogiCORE IP Product Guide Vivado Design Suite PG020 April 1, 2015 ”を参照のこと)
このAXI VDMA の仕様と Vivado HLS 2014.4 で作製するAXI4-Stream IP の仕様を合わせよう。今回作製するAXI4-Stream IP は、1ラインだけの動作サンプルなので、最後に TLAST をアサートする。そして、TUSER が 1 になったストリーム・データの時にフレームがスタートする。

tu1978 さんからサンプルを頂いたので、それを参照しながら作っていこう。(tu1978 さん、いつもありがとうございます~)

それによると、サイドチャネル付きの AXI4-Stream は、ap_axi_sdata.h をインクルードすれば良いそうだ。
ap_axi_sdata.h には、2つのテンプレートがあって、その違いは data が ap_int か ap_uint かの違いになる。 それぞれの構造体名は ap_axis と ap_axiu だ。 ap_axis のテンプレートを下に引用する。

template<int D,int U,int TI,int TD>
  struct ap_axis{
    ap_int<D>    data;
    ap_uint<D/8> keep;
    ap_uint<D/8> strb;
    ap_uint<U>   user;
    ap_uint<1>   last;
    ap_uint<TI>  id;
    ap_uint<TD>  dest;
  };


これを使用して、例によってインチキな myfilter() を書いてみた。その fil_aixs_side_ch.cpp を下に示す。

//
// fil_axis_side_ch.cpp
// 2015/04/29
//

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

#include "fil_axis_side_ch.h"

int my_filter(int sr0, int sr1, int sr2);

int fil_axis_side_ch(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> val;

    int sreg[3] = {};    // 0にクリア

#pragma HLS array_partition variable=sreg complete

    do {    // user が 1になった時にフレームがスタートする
        ins >> pix;
    } while(pix.user == 0);

    for (int i=0; i<HORIZONTAL_PIXEL_WIDTH; i++){
#pragma HLS PIPELINE
        for (int j=0; j<2; j++){
#pragma HLS UNROLL
                sreg[j] = sreg[j+1];
        }
        if (i != 0)    // 最初の入力はすでに入力されている
            ins >> pix;

        sreg[2] = pix.data;

        val.data = my_filter(sreg[0], sreg[1], sreg[2]);
        if (i == (HORIZONTAL_PIXEL_WIDTH-1))
            val.last = 1;
        else
            val.last = 0;

        outs << val;
    }

    return 1;
}

int my_filter(int sr0, int sr1, int sr2){
    int rv = -sr0 +2*sr1 -sr2;
    if (rv < 0){
        rv = 0;
    }

    return(rv);
}


なお、do { } while 文を下のメインループの for () 文の中に入れてしまうと、TREDY が1回のストリーム・データに付き1回ディアサートされてしまって、ストリームが wait してしまった。
このように、漫然とC++を書いているだけではダメで、書いたコードがどのようにハードウェアに影響を及ぼすのか?を考えながらコードを書いていく必要があるようだ。

次に、fil_aixs_side_ch.h を下に示す。

// fil_aixs_side_ch.h
// 2015/04/29

#define HORIZONTAL_PIXEL_WIDTH 50


これで、Vivado HLS 2014.4 で fil_aixs_side_ch プロジェクトを作製した。
lap_filter_AXIS_18_150429.png

最後に、テストベンチ用のC++ ファイル、fil_aixs_side_ch_tb.cpp を作製した。fil_aixs_side_ch_tb.cpp を下に示す。

// fil_axis_side_ch_tb.cpp
// 2015/04/29

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <ap_axi_sdata.h>

#include "fil_axis_side_ch.h"

int fil_axis_side_ch(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);

#define CLOCK_PERIOD 10

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    hls::stream<ap_axis<32,1,1,1> > outs;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;

    int m_seq = 1// M系列の値
    int i;
    int xor_shift;

    cout << "ins" << endl;

    for(i=0; i<5; i++){    // dummy data
           pix.user = 0;
         pix.data = i;
        ins << pix;
    }

    for(int i=0; i < HORIZONTAL_PIXEL_WIDTH; i++){
        //xor_shift = (m_seq>>31) ^ ((m_seq>>21)& 1) ^ ((m_seq>>1) & 1) ^ (m_seq & 1); // (32, 22, 2, 1)
        //m_seq = (m_seq<<1) | xor_shift;
        if ((i%CLOCK_PERIOD) < (CLOCK_PERIOD/2))
            m_seq = 0;
        else
            m_seq = 1000;

        pix.data = (ap_int<32>)m_seq;

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

        ins << pix;

        cout << hex << m_seq << endl;
    }


    fil_axis_side_ch(ins, outs);

    cout << endl;
    cout << "outs" << endl;
    for(int i=0; i < HORIZONTAL_PIXEL_WIDTH; i++){
        outs >> vals;
        ap_int<32> val = vals.data;
        cout << hex << val << endl;
        if (vals.last)
            cout << "AXI-Stream is end" << endl;
    }

    return 0;
}

  1. 2015年04月29日 04:21 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/3131-6b6f1969
この記事にトラックバックする(FC2ブログユーザー)