FC2カウンター FPGAの部屋 2015年04月
fc2ブログ

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

FPGAの部屋

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

Vivado HLS 2014.4 でサイドチャネル付き AXI4-Stream をテストする3(RTLシミュレーション)

Vivado HLS 2014.4 でサイドチャネル付き AXI4-Stream をテストする2(C++シミュレーション、高位合成)”の続き。

前回、サイドチャネル付き1次元俺様フィルタのC++シミュレーションと高位合成を行ったので、今回は、RTLシミュレーションを行って、ハードウェアでの動作を確認する。その際に、RTLのトレースファイルを出力させて、Vivado でシミュレーション波形の表示を行う。

Vivado HLS で Run C/RTL Cosimulation ボタンをクリックして、RTL Cosimulation を行う。(または、Solution メニューから Run C/RTL Cosimulation を選択する)

Vivado HLS で Run C/RTL Cosimulation ボタンをクリックすると、Co-simulation Dialog が出てくる。
Dump Trace を all に変更する。
lap_filter_AXIS_5_150425.png

Co-simulation が終了した。
lap_filter_AXIS_24_150429.png

結果は、C Simulation と同じだった。

次に、RTLシミュレーション波形を見てみよう。

Vivado 2014.4 を立ち上げる。

Vivado の下の入力領域に、下に示すように tcl コマンドを入力した。

cd C:/Users/Masaaki/Documents/Vivado_HLS/study/fil_axis_side_ch/solution1/sim/verilog/
current_fileset
open_wave_database fil_axis_side_ch.wdb
open_wave_config fil_axis_side_ch.wcfg

シミュレーション波形が表示された。
lap_filter_AXIS_26_150429.png

シミュレーション波形全体を表示した。その際に、ap_start と ap_done、ap_idle、 ap_NS_fsm をシミュレーション波形に追加した。
lap_filter_AXIS_27_150429.png

ap_NS_fsm は、Vivado HLS 2014.4 の Analysis タブ -> Resource タブで示された Control Step で示された C0 ~ C5 のステートを表しているようだ。何故か? C6 ステートは無い。
lap_filter_AXIS_23_150429.png

190 ns 周辺で、s_axi_AXILiteS_WVALID, s_axi_AXILiteS_WREADY 信号が共に 1 になっているので、AXI4 Lite Slave バス経由で、fil_axis_side_ch に s_axi_AXILiteS_WDATA の 1 が書かれている。これは、スタート信号となる。
少し遅れて、ap_start が 1 になっているが分かる。
スタート後に ap_NS_fsm が 000001 (C0) から 000010 (C1) → 000100 (C2) を3回繰り返している。これは、sreg[3] の初期化を行っているようだ。
その後、 ap_NS_fsm は 001000 (C3) ステートに移行して、TUSER が 1 になったデータを待つ。C++ ソースコードで言うと、do { } while 文で、pix.user が 1 になるのを待っている。
それが来ると、010000 (C4) ステートに移行して、AXI4-Stream で来るストリーム入力に俺様フィルタをかけていく。実際にフィルタ後の値を Write するのは 100000 (C5) ステートだが、パイプラインされているようである。
010000 (C4) ステートに移行した1クロック後で、ins_TREADYが1クロック期間、落ちている。これは、do { } while 文から for () 文に処理が切り替わり、今度は俺様フィルタの演算をし始めるので、そのパイプライン満たすためだと思う。
lap_filter_AXIS_28_150429.png

最後の部分を見ると、実践のカーソルの部分で、ap_done が出力され、ここでフィルタ動作が終了しているのが分かる。
AXI4 Lite Slave バスでは、ポーリングで Read を繰り返し行い、Done するのを監視している。破線のカーソル部分で、0x6 をRead してフィルタ動作の完了が分かった。
ap_done の 1 クロック前に、 ap_NS_fsm は 100000 (C5) ステートに移行している。ここでは、ストリーム入力が無くなったので、 100000 (C5) ステートのフィルタ演算+ストリーム出力が見えているのだろう。その後はIDLE ステートの 000001 (C0) に戻っている。
また、最後のストリーム出力のタイミングで、outs_TLAST が 1 になっているのが見える。
lap_filter_AXIS_29_150429.png

次は、AXI4-Stream IP として、ラプラシアンフィルタを作っていこう。
  1. 2015年04月30日 04:11 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 でサイドチャネル付き AXI4-Stream をテストする2(C++シミュレーション、高位合成)

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

今回は、サイドチャネル付き1次元俺様フィルタのC++シミュレーションと高位合成を行う。

最初は、Vivado HLS 2014.4 でC++ シミュレーションを行う。
Run C Simulation アイコンをクリックして、C Simulation を実行した後のVivado HLS 2014.4 の画面を下に示す。
lap_filter_AXIS_19_150429.png

標準出力結果を下に示す。

ins
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8

outs
0x0
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
AXI-Stream is end


C Synthesis アイコンをクリックして、高位合成を行った。
lap_filter_AXIS_20_150429.png

Latency と Interval がどちらも ? だった。やはり、TUSER を待っているので、どちらも分からない?ということであろう。
Utilization Estimates を示す。BRAM_18K とDSP48E は使用していない。
lap_filter_AXIS_21_150429.png

Analyze 画面を示す。真ん中下のResource タブをクリックしている。
C3ステートの read をクリックすると、do { } while 内部の AXI4-Stream 入力がヒットした。
lap_filter_AXIS_22_150429.png

C4ステートの read をクリックすると、メインの for () ループの AXI4-Stream 入力がヒットした。
lap_filter_AXIS_23_150429.png
  1. 2015年04月29日 16:27 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

「龍三と七人の子分たち」(映画)を見てきました

今日は一人で「龍三と七人の子分たち」(映画)(リンク先は音が出ます)を見てきました。
コメディでした。面白かったです。笑っちゃいました。でも、一般の人を脅すのは良くないですね。。。
  1. 2015年04月29日 13:02 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

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

シンデレラ(映画)を見てきました

今日は、下の娘と奥さんと一緒に、シンデレラ(映画)を見てきました。
映画は女性向けだと思いますが、男でも結構楽しめました。でも、やはり、女性と一緒にいくほうが良さそうです。
  1. 2015年04月26日 21:16 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

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

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

前回はfil_axis_sample を高位合成した。今回は、RTLシミュレーションを行う。その際に、RTLのトレースファイルを出力させて、Vivado でシミュレーション波形の表示を行う。
Vivado Design Suite Tutorial High-Level Synthesis UG871 (v2014.3) November 10, 2014”の 175 ページ、”Chapter 8: RTL Verification, Step 2: View the RTL Trace File in Vivado”を参考にした。

Vivado HLS で Run C/RTL Cosimulation ボタンをクリックして、RTL Cosimulation を行う。(または、Solution メニューから Run C/RTL Cosimulation を選択する)

Vivado HLS で Run C/RTL Cosimulation ボタンをクリックすると、Co-simulation Dialog が出てくる。
Dump Trace を all に変更する。
lap_filter_AXIS_5_150425.png

Co-simulation が終了した。
lap_filter_AXIS_6_150425.png

結果は、C Simulation と同じだった。

次に、RTLシミュレーション波形を見てみよう。

C:\Users\Masaaki\Documents\Vivado_HLS\study\fil_axis_sample\solution1\sim\verilog フォルダに fil_axis_sample.wcfg と fil_axis_sample.wdb が生成されているのが分かる。
lap_filter_AXIS_7_150425.png

Vivado 2014.4 を立ち上げる。
下の Tcl Console タブをクリックする。
lap_filter_AXIS_8_150425.png

すると、Tcl Console が開く。
下の入力領域に、
cd /Users/Masaaki/Documents/Vivado_HLS/study/fil_axis_sample/solution1/sim/verilog/
と入力する。私の環境では、Vivado HLS のプロジェクトと、Vivado のインストール・ディスクが一緒だったが、違う場合はドライブ名も付ける必要があると思う。(やっていないので不確かだ)
lap_filter_AXIS_9_150425.png

次に、
current_fileset
と入力した。すると、Vivado 2014.4 のGUI が変化した。
lap_filter_AXIS_10_150425.png

open_wave_database fil_axis_sample.wdb
と入力した。更に、GUI が変更されて、Scopes や Objects が表示された。
lap_filter_AXIS_11_150425.png

open_wave_config fil_axis_sample.wcfg
を実行すると、シミュレーション波形が表示された。
lap_filter_AXIS_12_150425.png

シミュレーション波形全体を表示した。その際に、ap_start と ap_done、ap_idle をシミュレーション波形に追加した。
lap_filter_AXIS_13_150425.png

カーソルの部分で、s_axi_AXILiteS_WVALID, s_axi_AXILiteS_WREADY 信号が共に 1 になっているので、AXI4 Lite Slave バス経由で、fil_axis_sample に s_axi_AXILiteS_WDATA の 1 が書かれている。これは、スタート信号となる。
少し遅れて、ap_start が 1 になっているが分かる。
lap_filter_AXIS_14_150425.png

次に、AXI4-Stream のデータ in_data_V_V_TDATA を見ると、点線のカーソルと実践のカーソル間で、すべて 0x3e8 になっている。これは、ap_clk からすると 5 クロック分だ。カーソルの差分も 50 ns となっている。
それに対して、out_data_V_V_TDATA を見ると、きちんVivado HLS のコンソールと同様の値が出ているのが分かる。これで、1クロックごとにAXI4-Stream データが出力されていることが分かった。
lap_filter_AXIS_15_150425.png

最後の部分を見ると、実践のカーソルの部分で、ap_done が出力され、ここでフィルタ動作が終了しているのが分かる。
AXI4 Lite Slave バスでは、ポーリングで Read を繰り返し行い、Done するのを監視している。破線のカーソル部分で、0x6 をRead してフィルタ動作の完了が分かった。
lap_filter_AXIS_16_150425.png

このように、Vivado HLS 2014.4 はテストベンチもVerilog HDL に変換してシミュレーションを行ってくれる(これは、Co-simulation Dialog の RTL Selection のラジオボタンで Verilog を指定しているからだ)
とても便利だと思う。これで 1 クロックごとにフィルタが動作するのを確認することができた。しかもアクセスパターンも実際の使用例に近いと思う。
  1. 2015年04月26日 04:29 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

FPGAマガジンNo.9 に記事を書きました

FPGAマガジンNo.9 に、”【365日間の評価ライセンスが取得できる!】 C言語からFPGAを開発できる高位合成ツールが無償で使える! Vivado WebPACK EditionとVivado HLS評価版のインストール手順”という記事を書きました。
これを見ると、Vivado WebPACKと Vivado HLS 評価版が同時にインストールできます。Vivado HLS 評価版は、評価期間が365日つまり1年なので、相当使えますよ。どちらも登録は必要ですが、無料で使えます。
Vivado HLS はC、C++、System C を高位合成で Verilog HDL や VHDL で構成されたIP にすることができます。
今、私がやっているVivado HLS 2014.4のプロジェクトもプロジェクトを作って、ブログに貼ってあるC や C++ ソースコードを貼り付けてファイルを作れば、そっくりそのまま試してみることができます。
Vivado WebPACKはVivado Simulator Logic Analyzer が使えないのが残念ですが、その他のVivado の Design Editionの機能は動作します(一部制限あり)。

Vivado Simulator を使う方法の中で、一番廉価なのは、Digilent 社のWebショップで、ZYBOと一緒に ZYBO Accessory Kit ($20) を購入する方法です。Zynq-7000 XC7Z010 のみのサポートですが、”Voucher for Xilinx Vivado® Design Suite: Design Edition”がついてきて、これを登録すると、Vivado Simulator Logic Analyzer が使えます。
日本では、ストロベリー・リナックスさんの”ZYBO Zynq-7000 Development Board+アクセサリキット”は ZYBO Accessory Kit が付いていると書いてあります。
  1. 2015年04月25日 13:09 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:7

Vivado HLS 2014.4 で AXI4-Stream をテストする3(1次元のフィルタのテスト2、C++シミュレーション、高位合成)

Vivado HLS 2014.4 で AXI4-Stream をテストする2(1次元のフィルタのテスト1、C++ソースコードの公開)”の続き。

前回は、C++ ソースコードを貼ったので、今回はそれをVivado HLS 2014.4 でC++ シミュレーションを行うことにした。

Run C Simulation アイコンをクリックして、C Simulation を実行した後のVivado HLS 2014.4 の画面を下に示す。
lap_filter_AXIS_2_150425.png

出力結果を下に示す。

Compiling ../../../fil_axis_sample_tb.cpp in debug mode
Generating csim.exe
in_data
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8
0
0
0
0
0
3e8
3e8
3e8
3e8
3e8

out_data
0x0
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
0x0
0x0
0x3e8
0x0
0x0
0x0
@I [SIM-1] CSim done with 0 errors.


C Synthesis アイコンをクリックして、高位合成を行った。
lap_filter_AXIS_3_150425.png

Latency が 58、Interval が 59 だった。
Utilization Estimates を示す。BRAM_18K とDSP48E は使用していない。
lap_filter_AXIS_4_150425.png

Analyze 画面を示す。
lap_filter_AXIS_17_150425.png

C1の phi_max, +, icmp は

int sreg[3] = {};

の処理で使用されていた。
C3の select, shl, icmp はmy_filter() の処理で使用されていた。
  1. 2015年04月25日 05:35 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で AXI4-Stream をテストする2(1次元のフィルタのテスト1、C++ソースコードの公開)

Vivado HLS 2014.4 で AXI4-Stream をテストする1(準備編)”の続き。

今度の目的は、AXI4-Stream を使ったラプラシアンフィルタをC++をソースコードとした高位合成で作製するのだが、さいしょの段階として、高位合成がどのようなHDLを生成するのかを確認するために最初に1次元のフィルタで試してみようと思う。

AXI4-Steramでラプラシアンフィルタを実装するためには、明示的にシフトレジスタを書く必要がある。その書き方は、”Vivado HLS ツールを使用し たビデオ処理用メ モ リ構造のインプリメント XAPP793 (v1.0) 2012 年 9 月 20 日”の3ページからに書いてある。この辺りを順次試しながら、ラプラシアンフィルタのAXI4-Stream版を試していこうと思う。

なお、ラプラシアンフィルタ のC++版ソースコードは、tu1978 さんがすでに、GitHubにあげてくれている(formalism/laplacian_filter_hls)。これを使わせて頂いても良いのだが、Vivado HLS 2014.4 の AXI4-Stream の作法の練習とともに、シフトレジスタの書き方の練習からやっていこうと思う。それに、C++ をよく知らないので、C++ の練習もやっていこうと思っている。

tu1987 さんのラプラシアンフィルタのC++ ソースコードを参考にさせて頂いて、インチキな myfilter() を書いてみた。トップのファイル名は fil_axis_sample.cpp とした。fil_axis_sample.cpp を下に示す。

//
// fil_axis_sample.cpp
// 2015/04/18
//

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

#include "fil_axis_sample.h"

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

int fil_axis_sample(hls::stream<ap_int<32> >& in_data, hls::stream<ap_int<32> >& out_data){
#pragma HLS INTERFACE axis port=in_data
#pragma HLS INTERFACE axis port=out_data
#pragma HLS INTERFACE s_axilite port=return

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

#pragma HLS array_partition variable=sreg complete

    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];
        }
        sreg[2] = in_data.read();

        int val = my_filter(sreg[0], sreg[1], sreg[2]);
        out_data.write(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);
}


fil_axis_sample.h はこれだけだ。

// fil_axis_sample.h

#define HORIZONTAL_PIXEL_WIDTH 50


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

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

// fil_axis_sample_tb.cpp

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

#include "fil_axis_sample.h"

int fil_axis_sample(hls::stream<ap_int<32> >& in_data, hls::stream<ap_int<32> >& out_data);

#define CLOCK_PERIOD 10

int main()
{
    using namespace std;

    hls::stream<ap_int<32> > in_data;
    hls::stream<ap_int<32> > out_data;
    int m_seq = 1// M系列の値
    int xor_shift;

    cout << "in_data" << endl;
    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;

        in_data.write((ap_int<32>)m_seq);
        cout << hex << m_seq << endl;
    }

    fil_axis_sample(in_data, out_data);

    cout << endl;
    cout << "out_data" << endl;
    for(int i=0; i < HORIZONTAL_PIXEL_WIDTH; i++){
        ap_int<32> val = out_data.read();
        cout << hex << val << endl;
    }

    return 0;
}


最初に、入力データをM系列で作ったが、出力がよく分からなかったので、矩形波で作りなおした。
  1. 2015年04月24日 05:41 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado 2014.4 でインプリメント時にStrategy を変更してインプリメント結果を改善する

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化16(性能が最大になる設定を探る8、追加2)”で、60 fps を超える速度でラプラシアンフィルタ処理を行うことができた。しかし、Vivado 2014.4 でインプリメントを行ったところタイミング制約のエラーが出てしまった。タイミング制約のエラーを無くしてみようと思う。

タイミング制約のエラーを無くす方法だが、少しくらいのタイミング制約エラーならば、インプリメント時のStrategy を変更してインプリメントを行う方法があるので、試してみた。(なお、twitter で hiyuh さんがつぶやいていたので、わかったのだった。 hiyuh さん、いつもお世話になっております。。。)

まず、そのやり方を説明しよう。

最初に、Vivado のFlow Navigator のImprementation -> Run Implementation を右クリックして右クリックメニューから Creater Implementaion Runs... を選択する。
Vivado_Strategy_1_150422.png

Create New Runs ダイアログが開く。
Vivado_Strategy_2_150422.png

Configure Implementaion Runs で、Strategy をクリックすると下にリストが出てくるので、適当なStrategy を選ぶ。
Vivado_Strategy_3_150422.png

Strategy を選択した。
Vivado_Strategy_4_150422.png

Launchi Options はデフォルトのままとした。
Vivado_Strategy_5_150422.png

Create New Runs Summary が表示された。Finish ボタンをクリックした。
Vivado_Strategy_6_150422.png

Design Runs ウインドウを見ると、インプリメントが始まっている。
Vivado_Strategy_7_150422.png

これで、25のStrategy が全て終了した。その結果だ。3つタイミング制約を満たしているStrategy がある。
Vivado_Strategy_8_150422.png

それは、Performance_ExplorePostRoutePhysOpt、Performance_ExploreSLLs、Performance_Retiming だった。
Strategy のカテゴリは、

Performance (パフォーマンス)
Area (エリア)
Power (消費電力)
Flow (フロー)
Congestion (密集)

の5つに分かれるようだ。
Vivado のインプリメントのStrategy については、”Vivado Design Suite ユーザーガイド インプリメンテーシ ョ ン UG904 (v2014.4) 2014 年 11 月 19 日”の124ページ、”インプリメンテーシ ョ ンのカテゴリ、 ス トラテジの説明、 および -directive の設定”を参照して欲しい。

一番、性能が良いPerformance_ExploreSLLs をアクティブにしてみよう。

Performance_ExploreSLLs を右クリックして右クリックメニューから、Make Active を選択する。
Vivado_Strategy_9_150422.png

Performance_ExploreSLLs がアクティブになった。
Vivado_Strategy_10_150422.png

これで、Vivado でビットストリームを行った。Performance_ExploreSLLs が選択されているのが、Worst Nagative Slack (WNS) からも分かる。
Vivado_Strategy_11_150422.png

これで、最速のラプラシアンフィルタ処理IP のプロジェクトもタイミング制約をクリアした。
  1. 2015年04月22日 05:08 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化16(性能が最大になる設定を探る8、追加2)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化15(性能が最大になる設定を探る8、追加1)”の続き。

前回は、コメント欄でラプラシアンフィルタのCソースコード例を示してくれたおるさんのコードを元に、tu1987さんが書いてくれたC++ソースコードをVivado HLS 2014.4 で高位合成し、IP化して、シミュレーションを行った。
今回は、インプリメントを行い、ZYBOでテストする。

前回、IP化したラプラシアンフィルタIP をV_ZYBO_CAMDfL_tu_2フォルダのV_ZYBO_CAMDfL143 プロジェクトのラプラシアンフィルタIP と入れ替えて、論理合成、インプリメント、ビットストリームの生成を行った。
lap_fil_hls_14_4_132_150420.png

タイミングエラーが出ている。Summary をすべて表示する。
lap_fil_hls_14_4_133_150420.png

FF と LUT は増えているが、DSP48Eが同じなのはなぜだろう?

タイミングエラーが出ているが、ZYBOで確認してみよう。
ハードウェアをエクスポートして、SDKを立ちあげ、BOOT.binを作製した。
BOOT.bin を Ubuntu の書かれた Micro SDカードにコピーして、ZYBOに挿入し電源ON。
lap_fil_1shot コマンドを実行して、ラプラシアンフィルタの処理時間を計測した。
lap_fil_hls_14_4_134_150420.png

その結果、ラプラシアンフィルタ全体の処理時間は 30.5 ms 、ラプラシアンフィルタのみの処理時間は 15.3 ms となった。タイミングエラーは出ているが、初めて、60 fps の 16.7 ms を上回った。
  1. 2015年04月21日 04:08 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化15(性能が最大になる設定を探る8、追加1)

コメント欄でラプラシアンフィルタのCソースコード例を示してくれたおるさんのコードを元に、tu1987さんがC++ソースコードを書いてくれた。C++ソースコード、laplacian_filer2.cpp はここにあります。
今回も(かな?)自分では何もしていない。とっても楽なこの環境で良いのだろうか?お二方に深く御礼申し上げます。

今回の修正は、一旦輝度に変換せずにRGB値で値を持って、使うときにRGB-Y変換をするということだ。使うときにRGB-Y変換をするので、RGB-Y変換モジュールは1つから3つに増えているはずだが、一旦、1ライン分、RGB-Y変換をするパスがなくなるので、速くなるはずだ。

Vivado HLS 2014.4 で lap_filter_axim_tu2_2014_4 フォルダを作って、高位合成を行った。
lap_fil_hls_14_4_128_150420.png

Utilization Estimates を下に示す。
lap_fil_hls_14_4_129_150420.png

修正前の tu1978さんのC++ソースコードを高位合成した後のUtilization Estimates はこれだ。


================================================================
== Utilization Estimates
================================================================
* Summary:
+-----------------+---------+-------+-------+-------+
| Name | BRAM_18K| DSP48E| FF | LUT |
+-----------------+---------+-------+-------+-------+
|Expression | -| 5| 0| 573|
|FIFO | -| -| -| -|
|Instance | 0| 4| 2244| 2804|
|Memory | 8| -| 0| 0|
|Multiplexer | -| -| -| 493|
|Register | -| -| 626| 4|
+-----------------+---------+-------+-------+-------+
|Total | 8| 9| 2870| 3874|
+-----------------+---------+-------+-------+-------+
|Available | 120| 80| 35200| 17600|
+-----------------+---------+-------+-------+-------+
|Utilization (%) | 6| 11| 8| 22|
+-----------------+---------+-------+-------+-------+

DSP48Eが 11 個から 41 個に増えている。FF や LUT も増えている。やはり演算器が増えた影響が効いているようだ。

シミュレーション用の Vivado 2014.4プロジェクトをコピーして、IP化した今回のラプラシアンフィルタ IP を使ってシミュレーションを行った。
lap_fil_hls_14_4_130_150420.png

AXI4 Master Read (M_AXI_CAM_FB) と AXI Master Write (M_AXI_LAP_FB) をよく見えるように拡大した。Write 間の間隔を測定すると、24.68 us だった。
lap_fil_hls_14_4_131_150420.png

600 ライン分のラプラシアンフィルタ処理を行った場合に、ラプラシアンフィルタ処理が完了するまでの時間を概算してみよう。

24.68 us x 600 ライン ≒ 14.8 ms

となり、60 fps の16.7 ms を上回る性能を示すという結果を得た。
  1. 2015年04月20日 04:53 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:2

連続ラプラシアンフィルタ処理

現在、ZYBO の Ubuntu 14.04 LTS上で使用している lap_fil_hls_1shot.c は、ラプラシアンフィルタ処理を1回だけ実行して、その実行時間を計測するソフトウェアだった。
そのソフトウェアを作製するには、Vivado HLS が生成したドライバ・ルーチン (xlap_filter_axim.c) を使っている。Vivado HLS のAXI4 - Lite Slave には、連続実行するためのビットがある。そのビットをEnable するには、XLap_filter_axim_EnableAutoRestart() を実行する。また、Disalbe するには、XLap_filter_axim_DisableAutoRestart() を実行すればよい。

XLap_filter_axim_EnableAutoRestart() を実行する時には、最初に XLap_filter_axim_Start() を実行する必要がある。

これらを使用して、連続実行させるためのソフトウェア lap_fil_hls_continue.c を作製した。lap_fil_hls_continue.c では、連続実行を Enable している間は、キーボードで s キーとリターン・キーを押されるのを待っていて、押されたら、連続実行を Disable する。

lap_fil_hls_continue を実行したら、連続的にラプラシアンフィルタを実行することができた。tu1987 さんのラプラシアンフィルタの高速化Cソースコードを使用した場合なので、50 fps のはずだが、カメラが 15 fps なので、問題無いようだ。
lap_fil_hls_14_4_127_150419.png

最後に、lap_fil_hls_continue.c を貼っておく。

// lap_fil_hls_continue.c
// by marsee
// 2015/04/19

#include "xlap_filter_axim.h"

#define CMA_START_ADDRESS           0x17800000
#define VIDEO_BUFFER_START_ADDRESS  0x18000000  // Limit 0x18800000, 800*600*4 = 2MBytes * 2
#define LAPLACIAN_FILTER_ADDRESS    0x18200000  // 800*600*4 = 0x1d4c00

int main() {
    unsigned int fb_addr, next_frame_addr;
    struct timeval start_time, temp1, temp2, end_time;
    XLap_filter_axim lap_fil_lsalve, frame_buf, bitmap_dispc;
    XLap_filter_axim *lap_fil_lsalvep, *frame_bufp, *bitmap_dispcp;
    u32 a;
    char line[256];
 
    //gettimeofday(&start_time, NULL); // No.1

    lap_fil_lsalvep = &lap_fil_lsalve;
    frame_bufp = &frame_buf;
    bitmap_dispcp = &bitmap_dispc;

    // Initialization of bitmap display controller
     if (XLap_filter_axim_Initialize(bitmap_dispcp, "bitmap_display_cntrler_axim") != XST_SUCCESS){
        fprintf(stderr, "bitmap_display_cntrler_axim open error\n");
        exit(-1);
    }
   
       a = *(volatile u32 *)(bitmap_dispcp->Bus_axi4ls_BaseAddress);
       //printf("%d\n", a);

    // Initialization of frame_buffer
    if (XLap_filter_axim_Initialize(frame_bufp, "frame_buffer_bmdc") != XST_SUCCESS){
        fprintf(stderr, "frame_buffer_bmdc open error\n");
        exit(-1);
    }
    
    fb_addr = (unsigned int)frame_bufp->Bus_axi4ls_BaseAddress + (unsigned int)(VIDEO_BUFFER_START_ADDRESS-CMA_START_ADDRESS);

    // frame buffer for laplacian filter result
    next_frame_addr = (unsigned int)frame_bufp->Bus_axi4ls_BaseAddress + (unsigned int)(LAPLACIAN_FILTER_ADDRESS-CMA_START_ADDRESS);
    
    // Initialization of lap_filter_axim
    if (XLap_filter_axim_Initialize(lap_fil_lsalvep, "lap_filter_axim_hls") != XST_SUCCESS){
        fprintf(stderr, "lap_filter_axim_hls open error\n");
        exit(-1);
    }

    //printf("%x\n", lap_fil_lsalvep->Bus_axi4ls_BaseAddress); fflush(stdout);
       a = *(volatile u32 *)(lap_fil_lsalvep->Bus_axi4ls_BaseAddress);
       //printf("%d\n", a); fflush(stdout);
    
    XLap_filter_axim_Set_cam_addr(lap_fil_lsalvep, (u32)VIDEO_BUFFER_START_ADDRESS);
    
    XLap_filter_axim_Set_lap_addr(lap_fil_lsalvep, (u32)LAPLACIAN_FILTER_ADDRESS);

    gettimeofday(&start_time, NULL); // No.2

    // Displayed the laplacian filter image
    *(volatile unsigned int *)bitmap_dispcp->Bus_axi4ls_BaseAddress = (unsigned int)LAPLACIAN_FILTER_ADDRESS;

    XLap_filter_axim_Start(lap_fil_lsalvep);
    XLap_filter_axim_EnableAutoRestart(lap_fil_lsalvep);

    do{
        printf("\nType <s>: quit ");
        gets(line);
    } while(!(line[0]=='s' || line[0]=='S')) ;
    
    XLap_filter_axim_DisableAutoRestart(lap_fil_lsalvep);
    
    gettimeofday(&end_time, NULL); // No.3
    
    if (XLap_filter_axim_Release(lap_fil_lsalvep) != XST_SUCCESS){
        fprintf(stderr, "lap_filter_axim_hls release error\n");
        exit(-1);
    }
    
    if (XLap_filter_axim_Release(frame_bufp) != XST_SUCCESS){
        fprintf(stderr, "frame_buffer_bmdc release error\n");
        exit(-1);
    }

    if (XLap_filter_axim_Release(bitmap_dispcp) != XST_SUCCESS){
        fprintf(stderr, "bitmap_display_cntrler_axim release error\n");
        exit(-1);
    }

    // Displayed the procee past time
    //gettimeofday(&end_time, NULL); // No.4
    
    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %ld.%6.6ld sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %ld.%6.6ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }

    return(0);
}

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

Vivado HLS 2014.4 で AXI4-Stream をテストする1(準備編)

今まで、ソフトウェアでも動作するCソースコードをハードウェアにオフロードしてアクセラレーションするということをやってきた。
ある程度は、アクセラレーションできたが、まだ 60fps のフレームレートに届かなかった。それは、AXI4 Master のバースト・アクセスをするmemcpy()関数が、後に続くラプラシアンフィルタ処理とオーバーラップできていないのが原因だと考えられる。これが実現できないと、これ以上の性能向上は望めない。
さて、Vivado HLS は60fps でのラプラシアンフィルタ処理はできないのか?と言うとそうではなくて、AXI4-Stream を使用する手段がある。むしろこちらのほうが本命なのだと思う。
なぜ本命なのか?というと、AXI4-Streamでは、DMAエンジンはXilinx社のVDMAやAXI_DMAを使って、AXI4-Stream出力を受けてラプラシアンフィルタ処理を行い、AXI4-Streamで出力してまたDMAでメモリにWriteするという構造になる。今までのAXI4-Master 版では、DMAエンジンごとCソースコードで書いていたが、今度はDMAエンジンは専用IPに任せて、ラプラシアンフィルタのアルゴリズム部分だけVivado HLS の高位合成で行うということになる。つまり、AXI4-Master 版でボトルネックになっていたmemcpy() はなくなるので、すべての処理を(たぶん)PIPELINE化できるということになる。

今までAXI4-Master 版のラプラシアンフィルタでは、メモリ・ベースで入出力を行ってきた(ソフトウェアはそうなので、それをハードウェアにしてもそうなる)が、これは、メモリの帯域をそれだけ使用してしまうことになる。これは、メモリ帯域がきつい4K2Kなどの高解像度の画像ではとっても大変だ。そこで、普通にハードウェアを作ってもそうだが、カメラからの入力ピクセルデータのストリームか、ディスプレイへの出力ピクセルデータのストリーム、どちらかに、ストリームでのフィルタ処理(入れることができれば?だが)を入れるのが一般的である。よって、通常はストリーム入出力 でラプラシアンフィルタ処理IP を作るのが一般的なのだ。これらの処理ではメモリからReadしてメモリにWriteしていないので、メモリ帯域を使用していない。

そこまでわかっているのに、私がなぜAXI4-Master でのラプラシアンフィルタ処理にこだわったかというと、ソフトウェアで動作するそのままで、ハードウェアにして性能向上を図ってみたいという1点に尽きる。残念ながら 60fps での実行は無理だったが、tu1978 さんに教えて頂いたCソースコードでは、20 ms つまり、50 fps まで行けたのだった。

次からは、AXI4-Stream でのフィルタの作り方を勉強していこう。これからは、Cソースコードというソフトウェアの書き方ではなく、例えば、シフトレジスタになるCやC++の記述を書いていくことになる。つまり、AXI4-Master では、どういう形のハードウェアなるのか?ということに思いを馳せても、ソフトウェアとして書いてきたが、AXI4-Streamでは、クロックによる遅延素子のレジスタを意識して書くことになるので、HDLを書いていることになるはずである。

と、ここまで書いて、1つやり残していることを忘れていた。今までラプラシアンフィルタを1回のみ実行していたのだが、Vivado HLS には連続実行する設定があるので、50 fps で連続してラプラシアンフィルタ処理をすることができるはずなのだ。それをやってからAXI4-Stream に移ろうと思う。
  1. 2015年04月19日 04:59 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:2

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化14(性能が最大になる設定を探る7、まとめ)

メモリベースのラプラシアンフィルタ処理のCソースコードを高位合成した際のラプラシアンフィルタの処理時間をまとめておこう。

PL の各IP に供給されるのは、FCLK_CLK0 の出力周波数は 100 MHz だ。

1.ディレクティブを与えていない状態でのラプラシアンフィルタの処理時間を示す。この時には、FCLK_CLK0からすべてのIP にクロックが供給されている。

Vivado HLS 2014.4で生成したラプラシアンフィルタIPをシミュレーション3(原因が分かった)
ZYBO Linux (Ubuntu 14.04 LTS) 上でMakefile を作ってラプラシアンフィルタIPの制御ソフトをコンパイル4(うまく行った)

項目
Vivado HLS 2014.4 のクロック周期制約10 ns
Vivado HLS 2014.4 のTiming Summary Estimated8.75 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slicenone
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFOnone
ラプラシアンフィルタIPのACLKやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数FCLK_CLK0に接続
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK0
ラプラシアンフィルタ全体の処理時間95.4 ms
ラプラシアンフィルタのみの処理時間80.0 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.00 倍














2.
ラプラシアンフィルタのCソース中の割り算をカウンタで置き換えて、2箇所にPIPELINEディレクティブを追加した状態でのラプラシアンフィルタの処理時間を示す。この時にも、FCLK_CLK0からすべてのIP にクロックが供給されている。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化2(PIPELINEディレクティブ)
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化3(PIPELINEディレクティブを実機テスト)

項目
Vivado HLS 2014.4 のクロック周期制約10 ns
Vivado HLS 2014.4 のTiming Summary Estimated8.75 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slicenone
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFOnone
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数FCLK_CLK0に接続
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK0
ラプラシアンフィルタ全体の処理時間71.4 ms
ラプラシアンフィルタのみの処理時間60.0 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.33 倍














3.
tu1987 さんのラプラシアンフィルタの高速化Cソースコードを使用した場合の処理時間を示す。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化4(tu1978さんのCソースコード)
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化5(tu1978さんのCソースコードを実機で検証)

項目
Vivado HLS 2014.4 のクロック周期制約10 ns
Vivado HLS 2014.4 のTiming Summary Estimated9.63 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slicenone
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFOnone
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数FCLK_CLK0に接続
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK0
ラプラシアンフィルタ全体の処理時間35.4 ms
ラプラシアンフィルタのみの処理時間20.0 ms
1. のラプラシアンフィルタのみの処理時間との性能比4.00 倍














4.
Vivado HLS 2014.4 で高位合成するときのクロック周期を短くして、動作周波数を高くして高速化しようとした。.tu1987 さんのラプラシアンフィルタの高速化Cソースコードはクロック周期を短くする余地がなかった。 2. のラプラシアンフィルタのCソース中の割り算をカウンタで置き換えて、2箇所にPIPELINEディレクティブを追加したCソースコードのクロック周期を短くした。クロック周期は、4 ns 、250 MHz まで、エラー無しに高位合成できた。但しVivado 2014.4 によるインプリメントでは、FCLK_CLK3 を 166.66667 MHz に設定した。そのFCLK_CLK3 をラプラシアンフィルタIPやそのAXIバスが接続されているAXI_Interconnect のAXIバスのインターフェースのクロックとして使用した。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化6(動作周波数のチューンナップ)
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化7(動作周波数のチューンナップを実機で検証)

項目
Vivado HLS 2014.4 のクロック周期制約4 ns
Vivado HLS 2014.4 のTiming Summary Estimated3.94 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slicenone
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFOnone
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数166.66667 MHz
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK0
ラプラシアンフィルタ全体の処理時間78.4 ms
ラプラシアンフィルタのみの処理時間63.0 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.23 倍















5.Vivado HLS 2014.4 で高位合成するときの制約を 10 ns に戻した。これで、Vivado 2014.4 でインプリメントをした時の slack の余裕に注目した。 slack の余裕から 120 MHz の動作周波数で動きそうだったので、PSのクロック設定で設定できた 118.2 MHz でラプラシアンフィルタIP を駆動した。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化8(性能が最大になる設定を探る)

項目
Vivado HLS 2014.4 のクロック周期制約10 ns
Vivado HLS 2014.4 のTiming Summary Estimated8.75 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slicenone
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFOnone
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数118.2 MHz
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK0
ラプラシアンフィルタ全体の処理時間62.8 ms
ラプラシアンフィルタのみの処理時間47.4 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.69 倍
















6. 5. から AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slice を Auto、Enable Data FIFO を 32 deep に変更した。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化8(性能が最大になる設定を探る)

項目
Vivado HLS 2014.4 のクロック周期制約10 ns
Vivado HLS 2014.4 のTiming Summary Estimated8.75 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register SliceAuto
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFO32 deep
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数118.2 MHz
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK0
ラプラシアンフィルタ全体の処理時間62.7 ms
ラプラシアンフィルタのみの処理時間47.4 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.69 倍
















7. 6. から、axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースを FCLK_CLK3 に変更した。つまり、PSのAXIバスとAXI Interconnect の全体のクロックの周波数を 118.2 MHz とした。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化9(性能が最大になる設定を探る2)

項目
Vivado HLS 2014.4 のクロック周期制約10 ns
Vivado HLS 2014.4 のTiming Summary Estimated8.75 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register SliceAuto
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFO32 deep
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数118.2 MHz
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK3
ラプラシアンフィルタ全体の処理時間62.7 ms
ラプラシアンフィルタのみの処理時間47.4 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.69 倍















8.Vivado HLS でクロック周期を 8 ns にして、FCLK_CLK3 を 130 MHz に設定した。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化11(性能が最大になる設定を探る4)

項目
Vivado HLS 2014.4 のクロック周期制約8 ns
Vivado HLS 2014.4 のTiming Summary Estimated7 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register SliceAuto
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFO32 deep
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数130 MHz
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK3
ラプラシアンフィルタ全体の処理時間63.7 ms
ラプラシアンフィルタのみの処理時間48.6 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.65 倍















9. 8. からから AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slice を none、Enable Data FIFO を none に変更した。但し、タイミング制約を満足していない。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化12(性能が最大になる設定を探る5)

項目
Vivado HLS 2014.4 のクロック周期制約8 ns
Vivado HLS 2014.4 のTiming Summary Estimated7 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slicenone
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFOnone
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数130 MHz
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK3
ラプラシアンフィルタ全体の処理時間64.1 ms
ラプラシアンフィルタのみの処理時間48.9 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.64 倍
















10. 9. から AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slice を Auto、Enable Data FIFO を 512 deep に変更した。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化13(性能が最大になる設定を探る6)

項目
Vivado HLS 2014.4 のクロック周期制約8 ns
Vivado HLS 2014.4 のTiming Summary Estimated7 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register SliceAuto
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFO512 deep
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数130 MHz
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK3
ラプラシアンフィルタ全体の処理時間64.2 ms
ラプラシアンフィルタのみの処理時間49.0 ms
1. のラプラシアンフィルタのみの処理時間との性能比1.63 倍















これで、ラプラシアンフィルタをいろいろなクロック周期制約で Vivado HLS 2014.4 で高位合成し IP 化した時に、Vivado 2014.4 でまたいろいろとパラメータの変更した時の性能差をまとめられた。
一番性能が良いのが、3. tu1987 さんのCソースコードを使った時で、これが 1. に比べて 4倍だった。
私のPIPELINEディレクティブを与えたCソースコードで最速なのは、5. 6. 7. の 1.69 倍だった。いずれも Vivado HLS のクロック周期制約を 10 ns で、ラプラシアンフィルタ IP の動作周波数を 118.2 MHz にした時だった。

(追加)
完全にパイプライン化されていれば結果は違ったと思うが、シリアライズされているところがあるので、クロック周期を短くすると、周波数の増加以上にレイテンシが増えてしまったようだ。

(2015/04/21:追記)
11.コメント欄でラプラシアンフィルタのCソースコード例を示してくれたおるさんのコードを元に、tu1987さんがC++ソースコードを書いてくれた。
3. の tu1978 さんのC++ソースコードを修正して頂いた。今回の修正は、一旦輝度に変換せずにRGB値で値を持って、使うときにRGB-Y変換をするということだ。使うときにRGB-Y変換をするので、RGB-Y変換モジュールは1つから3つに増えているはずだが、一旦、1ライン分、RGB-Y変換をするパスがなくなるので、速くなるはずだ。
結果として、ラプラシアンフィルタ全体の処理時間は 30.5 ms 、ラプラシアンフィルタのみの処理時間は 15.3 ms となった。タイミングエラーは出ているが、初めて、60 fps の 16.7 ms を上回った。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化15(性能が最大になる設定を探る8、追加1)
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化16(性能が最大になる設定を探る8、追加2)

項目
Vivado HLS 2014.4 のクロック周期制約10 ns
Vivado HLS 2014.4 のTiming Summary Estimated9.63 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slicenone
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFOnone
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数FCLK_CLK0に接続
axi_mem_intercon_1のACLKとPS のS_AXI_HP2_ACLKのクロック・リソースFCLK_CLK0
ラプラシアンフィルタ全体の処理時間30.5 ms
ラプラシアンフィルタのみの処理時間15.3 ms
1. のラプラシアンフィルタのみの処理時間との性能比5.23 倍











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

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化13(性能が最大になる設定を探る6)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化12(性能が最大になる設定を探る5)”の続き。

今回は、ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の設定は、Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 512 deep に設定した。

今回の設定を下の表にまとめておく。
項目
Vivado HLS 2014.4 のクロック周期制約8 ns
Vivado HLS 2014.4 のTiming Summary Estimated7 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register SliceAuto
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFO512 deep (packet mode)
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数130MHz, 100MHz








ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの設定を下に示す。
lap_fil_hls_14_4_117_150411.png

最初に、FCLK_CLK3 を 130 MHz に設定した。
lap_fil_hls_14_4_94_150407.png

論理合成、インプリメント、ビットストリームの生成を行った。Summary を下に示す。
lap_fil_hls_14_4_118_150412.png

AXI Interconnect (axi_mem_intercon_1) の設定が none, none の場合よりも、FFで 2 %, LUT で 4 %, BRAM で 5 % 増えている。

ZYBO 実機で、ラプラシアンフィルタ全体の処理時間を測定した結果、64.2 ms となった。ラプラシアンフィルタのみの処理時間は、49.0 ms だった。
lap_fil_hls_14_4_119_150412.png

Enable Data FIFO を 32 deep にした時よりも、両方共、約 100 ns 遅い。

次に、FCLK_CLK3 を 100 MHz に設定する。
lap_fil_hls_14_4_97_150408.png

論理合成、インプリメント、ビットストリームの生成を行った。Summary を下に示す。
lap_fil_hls_14_4_120_150412.png

130 MHz の時と同様に、AXI Interconnect (axi_mem_intercon_1) の設定が none, none の場合よりも、FFで 2 %, LUT で 4 %, BRAM で 5 % 増えている。

ZYBO 実機で、ラプラシアンフィルタ全体の処理時間を測定した結果、79.1 ms となった。ラプラシアンフィルタのみの処理時間は、63.7 ms だった。
lap_fil_hls_14_4_121_150412.png

Enable Data FIFO を 32 deep にした時よりも、両方共 約 300 ns 遅い

AXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の設定のうちのSlave Interface タブの Enable Data FIFO を 32 deep に設定しても、512 deep に設定も大した違いは無いと思う。
  1. 2015年04月13日 04:32 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:6

ソロモンの偽証 後篇・裁判 (映画)を見てきました

ソロモンの偽証 後篇・裁判 (映画)を昨日、見てきました。
前編は、とっても面白く、手に汗握る展開でこれからどうなるのか?と思って、ワクワクしながら後編を見たのですが、少し期待はずれかな?と思いました。小説は読んでません。中学生の演技は少したどたどしいところが良かったです。
  1. 2015年04月12日 06:12 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化12(性能が最大になる設定を探る5)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化11(性能が最大になる設定を探る4)”の続き。

前回は、FSBLをリビルドさせることで、ラプラシアンフィルタIP の動作周波数の切り替えが上手く行った。ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の設定は、Slave Interface タブの Enable Register Slice(タイミングの分離をする仕組みのようです) を Auto に、Enable Data FIFO を 32 deep に設定した。32 deep だと、LUT を SRAM として使用するFIFOを使うので、LUT の使用数が増える可能性があると思う。なお、Master Interace タブはすべて none に設定してある。
lap_fil_hls_14_4_87_150405.png

今回は、Register Slice と Data FIFO のラプラシアンフィルタ処理性能に寄与する割合を確かめるために、2つともデフォルトの none に設定した状態での性能を比較する。

ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの設定を下に示す。すべて none とした。
lap_fil_hls_14_4_111_150410.png

今回の設定をまとめておく。
項目
Vivado HLS 2014.4 のクロック周期制約8 ns
Vivado HLS 2014.4 のTiming Summary Estimated7 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slicenone
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFOnone
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数130MHz, 100MHz









最初に、FCLK_CLK3 を 130 MHz に設定した。
lap_fil_hls_14_4_94_150407.png

論理合成、インプリメント、ビットストリームの生成を行った。Summary を下に示す。
lap_fil_hls_14_4_114_150411.png

タイミング制約が間に合っていない。
AXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 32 deep に設定した場合のSummary を下に示す。
lap_fil_hls_14_4_116_150411.png

タイミング制約が間に合っている他、FF、LUT、Memory LUT がそれぞれ 2 % ずつ多くなっている。

タイミング制約を満たしていないが、ZYBO 実機で、ラプラシアンフィルタ全体の処理時間を測定した結果、64.1 ms となった。ラプラシアンフィルタのみの処理時間は、48.9 ms だった。
lap_fil_hls_14_4_115_150411.png

AXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 32 deep に設定した場合のラプラシアンフィルタ全体の処理時間は 63.7 ms だった。ラプラシアンフィルタ処理のみの時間は 48.6 ms だった。

ラプラシアンフィルタ処理のみの時間の双方を比較すると、AXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 32 deep に設定した場合が 300 us ほど速い。
この 300 us / 垂直 600 本 = 500 ns (垂直1本あたりの改善時間)
これを、130 MHz の周期 7.69 ns で割ると、500 ns / 7.69 ns ≒ 65 クロッ分ク改善している。(AXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 32 deep に設定した場合の方が、していない場合よりも 65 クロック分改善している)

次に、ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの設定をすべて none とした時に、FCLK_CLK3 を 100 MHz に設定する。
lap_fil_hls_14_4_111_150410.png

lap_fil_hls_14_4_97_150408.png

論理合成、インプリメント、ビットストリームの生成を行った。Summary を下に示す。
lap_fil_hls_14_4_112_150410.png

FF、LUT、Memory LUT は 130 MHz と同様に 2 % 減少している。

ZYBO 実機で、ラプラシアンフィルタ全体の処理時間を測定した結果、78.8 ms となった。ラプラシアンフィルタのみの処理時間は、63.9 ms だった。
ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の設定で Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 32 deep に設定した時のラプラシアンフィルタ処理全体の経過時間は 78.4 ms だった。ラプラシアンフィルタ処理のみの経過時間は 63.0 ms だった。
lap_fil_hls_14_4_113_150410.png 
ラプラシアンフィルタ処理のみの時間の双方を比較すると、AXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 32 deep に設定した場合が 900 us ほど速い。
この 900 us / 垂直 600 本 = 1.5 us (垂直1本あたりの改善時間)
これを、100 MHz の周期 10 ns で割ると、1500 ns / 10 ns = 150 クロッ分ク改善している。(AXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 32 deep に設定した場合の方が、していない場合よりも 150 クロック分改善している)

130 MHz でラプラシアンフィルタIP を動作させた時と、100 でラプラシアンフィルタIP を動作させた時のデータが大幅に違っているが、いずれも、ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の設定で、Slave Interface タブの Enable Register Slice を Auto に、Enable Data FIFO を 32 deep に設定した時の方が性能が良かった。
  1. 2015年04月11日 23:59 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化11(性能が最大になる設定を探る4)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化10(性能が最大になる設定を探る3)”の続き。

前回は、本当に FCLK_CLK3 の周波数を変更しても変化がないかどうかを調べてみることにした。方法としては、Vivado HLS 2014.4 でクロック周期を 8 ns に変更し、回路を変更してテストした。その結果として、クロック周波数を変えてもラプラシアンフィルタ処理時間に変化が無かった。

昨日、tu1978 さんからFSBL がリビルドされていないのではないか?というアドバスを頂いた。
確かに、PSのクロックを変更したが、これはFPGA が変更されたのではなくて、PS、つまりARM SoC 部分のPL に出力するクロック周波数を変更した訳だ。つまり、ビットストリームが PS のクロック周波数に関係するのではなく、FSBL(First Stage Boot Loader) が関係する訳だ。今回は、FSBL がきちんと変更されているのかを調べてみることにした。
(注: PS から来るクロックの周波数が変化したことにより、PL(FPGA)部分もそのクロック周波数に合わせて、論理合成、インプリメント、ビットストリームの生成を行っています)

今回の設定を下の表にまとめておく。
項目
Vivado HLS 2014.4 のクロック周期制約8 ns
Vivado HLS 2014.4 のTiming Summary Estimated7 ns
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register SliceAuto
AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブのEnable Data FIFO32 deep
ラプラシアンフィルタIPやaxi_mem_intercon_1へ供給するFCLK_FCLK3の周波数130MHz, 100MHz










SDKを立ちあげて、FSBLプロジェクトを右クリックし、右クリックメニューから Properties を選択する。
lap_fil_hls_14_4_101_150409.png

Properties for FSBL ダイアログのLined Resources をクリックし、Linked Resources タブをクリックする。
すると、ps7_init.c のLocation がV_ZYBO_CAMDfL_wrapper_hw_platform_1 にあるのが見えた。
lap_fil_hls_14_4_102_150409.png

現在のHardware Platform Specification は3つあって、現在は、V_ZYBO_CAMDfL_wrapper_hw_platform_2 を使用している。よって、FSBLの参照している Hardware Platform Specification が違っている。
lap_fil_hls_14_4_103_150409.png

ちなみに、”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化5(tu1978さんのCソースコードを実機で検証)”の時点では、Hardware Platform Specification は2つだったので、高速化を検証しようとして、V_ZYBO_CAMDfL144_org をコピーして作製した時に、ハードウェアをエクスポートしたら 、V_ZYBO_CAMDfL_wrapper_hw_platform_2 ができたようだ。
Hardware Platform Specification が新しくできた場合は、プロジェクトをBSP ごと作り直す必要がある。

今回は、SDK のFSBL , FSBL_bsp , V_ZYBO_CAMDfL_wrapper_hw_platform_0 , V_ZYBO_CAMDfL_wrapper_hw_platform_1 を消去して、新たにFSBL プロジェクトを新規作成した。その様子を下図に示す。Hardware Platform も V_ZYBO_CAMDfL_wrapper_hw_platform_2 と表示されている。
lap_fil_hls_14_4_104_150409.png

SDKのプロジェクトは下図の様な構成になった。
lap_fil_hls_14_4_105_150409.png

この状態で、BOOT.bin を生成した。
lap_fil_hls_14_4_106_150409.png

ZYBO実機でテストしたところ、ラプラシアンフィルタ全体の処理時間は 63.7 ms となった。ラプラシアンフィルタ処理のみの時間は 48.6 ms となった。なお、FCLK_CLK3 は 130 MHz だ。
lap_fil_hls_14_4_107_150409.png

前回の結果と違う結果が出て良かった。

次に、Vivado でPL を変更して、ハードウェアをエクスポートしたら、きちんとFSBL もリビルドされるかどうかをテストする。

もう一度、FCLK_CLK3 を 100 MHz に設定した。
lap_fil_hls_14_4_97_150408.png

これで、Vivado で論理合成、インプリメント、ビットストリームの生成を行った。
ハードウェアをエクスポートして、SDK を起動した。
SDKを起動すると、かなり長い時間リビルドしていた。これが正常だと思う。以前のはリビルド時間が短すぎた。それに気づけなかったのが残念。。。
lap_fil_hls_14_4_108_150410.png

FSBL もリビルドされたようだ。
lap_fil_hls_14_4_109_150410.png

BOOT.bin を生成して、ZYBO実機でテストした。

ラプラシアンフィルタ全体の処理時間は 78.4 ms となった。ラプラシアンフィルタ処理のみの時間は 62.9 ms となった。
lap_fil_hls_14_4_110_150410.png

これは前回と同様の値だ。これで、ハードウェアをエクスポートして、SDKを起動するときにFSBL もリビルドされているのが確認できた。

ラプラシアンフィルタ処理のみの時間で高速化を検証してみよう。
100 MHz の動作周波数を 130 MHz にしたので、クロック周波数は 1.3 倍になった。
ラプラシアンフィルタ処理のみの時間は、62.9 ms が 48.6 ms になったので、処理時間は0.773 倍になった。
62.9/48.9 ≒ 1.294 倍なので、ほぼクロック周波数の比率が反映されている。

PIPELINEディレクティブを入れただけのクロック周期制約 10 ns のVivado HLS 2014.4で、ラプラシアンフィルタIP を 100 MHz で駆動した時のラプラシアンフィルタ全体の処理時間は 71.4 ms だった。ラプラシアンフィルタ処理のみの時間は 60.0 ms だった。
今回のラプラシアンフィルタIP の高位合成時のクロック周期の制約は、8 ns だった。その分、レイテンシが増えて、同じ 100 MHz でのラプラシアンフィルタ処理のみの実行時間は 62. 9 ms となった。
性能は、 60 / 62.9 ≒ 0.954 倍になった。
しかし、ラプラシアンフィルタIP を 130 MHz で動作させると、性能は 60 / 48.6 ≒ 1.235 倍になった。

検算をしてみよう。 1.235 / 0.954 ≒ 1.295 で、概ね正しいようだ。

つまり、以前よりも高位合成結果が 5 % 位、レイテンシが増加したが、クロック周波数が 1.3 倍になったので、総合的な性能は 1.235 倍になった。つまり、性能が向上した。

但し、比較対象に比べて今回は、ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect の設定を変更していることを忘れてはならない。
lap_fil_hls_14_4_87_150405.png
  1. 2015年04月10日 05:15 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化10(性能が最大になる設定を探る3)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化9(性能が最大になる設定を探る2)”の続き。

この記事は間違っています。この記事は参照せずに下のリンクの記事をご覧下さい。
以下のバグは、FSBLが正しい Hardware Platform Specification を参照していないのが原因でした。
詳しくは、”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化11(性能が最大になる設定を探る4)”を参照下さい。


前回は、PS の S_AXI_HP2_ACLK と S_AXI_HP2 につながる AXI Interconnect (axi_mem_intercon_1) の M00_ACLK を FCLK_CLK3 で駆動することにして、FCLK_CLK3を 118.2 MHz にしても 100 MHz の時とラプラシアンフィルタ処理時間に変化がなかった。(FCLK_CLK0 は 100 MHz )

今回は、本当に FCLK_CLK3 の周波数を変更しても変化がないかどうかを調べてみることにした。方法としては、Vivado HLS 2014.4 でクロック周期を変更し、回路を変更してテストしてみようと思う。

Vivado HLS でクロック周期を 8 ns にした。
lap_fil_hls_14_4_93_150407.png

高位合成し、IP化した。ZYBO_CAMDfL144_org フォルダのプロジェクトのラプラシアンフィルタIP にコピーして、FCLK_CLK3 を 130 MHz に設定した。
lap_fil_hls_14_4_94_150407.png

論理合成、インプリメント、ビットストリームの生成を行った。
lap_fil_hls_14_4_95_150407.png

これで、間を飛ばすが、ZYBOでラプラシアンフィルタ処理を行った。
lap_fil_hls_14_4_96_150407.png

ラプラシアンフィルタ処理全体の経過時間は 78.1 ms だった。ラプラシアンフィルタ処理のみの経過時間は 63.0 ms だった。


次に、FCLK_CLK3 を 100 MHz に設定した。
lap_fil_hls_14_4_97_150408.png

論理合成、インプリメント、ビットストリームの生成を行った。
lap_fil_hls_14_4_98_150408.png

また間を飛ばすが、ZYBOでラプラシアンフィルタ処理を行った。
lap_fil_hls_14_4_99_150408.png

ラプラシアンフィルタ処理全体の経過時間は 78.4 ms だった。ラプラシアンフィルタ処理のみの経過時間は 63.0 ms だった。

結果としては、FCLK_CLK3 の周波数が 130 MHz でも 100 MHz でもラプラシアンフィルタの処理時間はほとんど変化が無い。これはどういうことだろうか???
  1. 2015年04月09日 04:15 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化9(性能が最大になる設定を探る2)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化8(性能が最大になる設定を探る)”の続き。

2015/04/15:バグがあったので、ブログ記事を書き換えました。
以前のバグは、FSBLが正しい Hardware Platform Specification を参照していないのが原因でした。
詳しくは、”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化11(性能が最大になる設定を探る4)”を参照下さい。


前回は、PS の S_AXI_HP2_ACLK と S_AXI_HP2 につながる AXI Interconnect (axi_mem_intercon_1) の M00_ACLK は 100 MHz のクロックを供給していた。それは、PS の AXI_HP2 ポートが 64 ビット幅のデータバスで、ラプラシアンフィルタ IP の32ビット幅のデータバス幅と比べると2倍のデータバス帯域があると思ったからだ。
しかし、うまく性能が出ていないので、今度は、PS の S_AXI_HP2_ACLK と S_AXI_HP2 につながる AXI Interconnect (axi_mem_intercon_1) の M00_ACLK を FCLK_CLK3 で駆動することにした。
現在、全体のAXI バスのクロックは、FCLK_CLK0 の 100 MHz で、、PS の S_AXI_HP2_ACLK と S_AXI_HP2 につながる AXI Interconnect (axi_mem_intercon_1) と ラプラシアンフィルタ IP のクロックは FCLK_CLK3 の 118.2 MHz となった。
lap_fil_hls_14_4_90_150407.png

これで、論理合成、インプリメント、ビットストリームの生成を行った。
lap_fil_hls_14_4_91_150407.png

以前同様に BOOT.bin を作製して、microSDカードに書き込んで、ZYBOに挿入して電源をON。
ラプラシアンフィルタ IP を起動したところ、ラプラシアンフィルタ全体の処理時間は 62.3 ms 、ラプラシアンフィルタのみ処理時間は 47.1 ms だった。
lap_fil_hls_14_4_126_150415.png

前回、PS のFCLK_CLK3 を100 MHz に設定していた時は、ラプラシアンフィルタ処理全体の時間は 62.7 ms 、ラプラシアンフィルタのみの処理時間は 47.4 ms だったので、300 us ほど良い結果になった。

同じ処理時間になる理由がよくわからない???
  1. 2015年04月08日 04:29 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

奥さんのお父さんのお葬式

今日は奥さんのお父さんのお葬式でした。
生前はとても良くして頂いたので、とっても残念です。
去年の4月にすい臓がんで余命2ヶ月と言われましたが、約1年間生きられて、直系の孫の大学と高校の入学を知ることができたので良かったのではないでしょうか?
ご冥福をお祈りします。残されたお母さんが心配ですが、まめに訪問しようと思います。
  1. 2015年04月06日 21:11 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化8(性能が最大になる設定を探る)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化7(動作周波数のチューンナップを実機で検証)”の続き。

以下のバグは、FSBLが正しい Hardware Platform Specification を参照していないのが原因でした。詳しくは、”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化11(性能が最大になる設定を探る4)”を参照下さい。

2015/04/14:このブログは正しいデータに書きなおしました。

前回は、クロック周期が 4 ns つまり、動作周波数 250MHz の制約でVivado HLS 2014.4 を使用して高位合成したラプラシアンフィルタIP をインプリメントしてみたら、166.66667 MHz でしか動作しなかった。しかも、パイプライン段数が増えて、パイプライン化されていないところもあるため大幅に処理時間が増えてしまった。(71.4 ms から 119.8 ms 78.4 ms に増えた)

今回は、Vivado HLS 2014.4 のクロック周期制約を変えてみて、一番性能の出る辺りはどこか?を調査してみよう。

まずは、クロック周期が 10 ns でもう一度テストしてみる。以前にテストした時とは、ラプラシアンフィルタIP をそれを担当するAXI Interconnect のクロック供給源がPS の FCLK_CLK3 に変更されているので、それが影響しているか?を探ってみた。
その結果、ラプラシアンフィルタ処理コマンド全体の処理時間は、71.2 ms で変化が無かった。周波数変換をしてもロスが少ないのだろうか? 但し、ラプラシアンフィルタのみの処理時間は 55.9 ms で、FCLK_CLK3を使用しなかった場合の 60.0 ms に比べて、なぜか速くなっている。
lap_fil_hls_14_4_124_150414.png

次に、Vivado HLS 2014.4 のタイミング・リポートで、FCLK_FCLK3 がどのくらいの余裕があるかを調べた。
lap_fil_hls_14_4_82_150405.png

その結果、1.691 ns の余裕があることが分かった。現在のクロック周期は 10 ns なので、計算してみると、

1 / (10 ns - 1.691 ns) x 1000 ≒ 120.4 MHz

よって、120 MHz にFCLK_CLK3 を設定してみよう。

ブロック・デザインで PS をダブルクリックして、設定ダイアログを開く。
lap_fil_hls_14_4_84_150405.png

FCLK_FCLK3 に 120 MHz を設定すると IO PLL では、実際の周波数は125 MHz になってしまった。Clock Source を ARM PLL に変更すると、118.2 MHz 程度になったので、これでやってみよう
lap_fil_hls_14_4_83_150405.png

これでブロック・デザインをセーブして、論理合成、インプリメント、ビットストリームの生成を行った。
lap_fil_hls_14_4_85_150405.png

ハードウェアをエクスポートし、SDKを立ちあげてブートイメージ (BOOT.bin) を作製した。
ブートイメージをMicro SDカードに書いて、ZYBOに挿入して、電源をONした。
カメラ画像を表示して、ラプラシアンフィルタ処理を行った。
FCLK_CLK3 を 118.2 MHz とした時のラプラシアンフィルタ処理全体の時間は 62.8 ms 、ラプラシアンフィルタのみの処理時間は 47.4 ms だった。やはり速くなっている。
lap_fil_hls_14_4_123_150414.png 

PIPELINEディレクティブを使って 100 MHz でラプラシアンフィルタIP を駆動した時のラプラシアンフィルタ全体の処理時間を計測したところ、約 71.4 ms で、ラプラシアンフィルタ処理時間のみを計測してみたところ、約 60.0 ms だった。
ラプラシアンフィルタ処理時間のみを比較してみよう。
60.0 ms / 47.4 ms ≒ 1.266 倍
1.18倍だと思っていただが、性能向上がクロック周波数の倍率よりも多い。なぜだ?

100 MHz でもラプラシアンフィルタのみの処理時間は、4.1 ms だけ、FCLK_CLK3を使用しない場合よりも速いということがわかったので、その分を足してみる。
60.0 ms / (47.4 + 4.1) ≒ 1.165 倍
このくらいだと、それらしいかもしれない。

FCLK_CLK3 を 118.2 MHz としてもラプラシアンフィルタ処理全体の時間は、71.3 ms で以前と変化が無かった。

どうもおかしいので、ラプラシアンフィルタIP のAXI4 Masterポートが入力されているAXI Interconnect の axi_mem_intercon_1 の設定を変えてみた。
lap_fil_hls_14_4_88_150405.png

lap_fil_hls_14_4_87_150405.png

これで、論理合成、インプリメント、ビットストリームの生成を行い、手順を踏んで実機でテストしてみた。が、ラプラシアンフィルタ処理全体の時間は、71.1 ms であまり変わっていない?

ラプラシアンフィルタ処理全体の時間は 62.7 ms 、ラプラシアンフィルタのみの処理時間は 47.4 ms だった。ラプラシアンフィルタIP のAXI Master が接続されているAXI Interconnect (axi_mem_intercon_1) の Slave Interface タブの設定をすべて none とした時とほとんど変化がなかった。
lap_fil_hls_14_4_125_150414.png

ちなみに、ラプラシアンフィルタIP のAXI4 Master 出力ポートのデータバスの幅は 32 ビットで、PS へ行くAXI Interconnect のAXI4 バスのデータバス幅は 64 ビットです。よって、PSへ行くデータバス帯域は動作周波数がラプラシアンフィルタIP と同一周波数の時は2倍あるはずです。
  1. 2015年04月05日 21:12 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化7(動作周波数のチューンナップを実機で検証)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化6(動作周波数のチューンナップ)”の続き。

以下のバグは、FSBLが正しい Hardware Platform Specification を参照していないのが原因でした。詳しくは、”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化11(性能が最大になる設定を探る4)”を参照下さい。

PIPELINEディレクティブに依って高速化したラプラシアンフィルタIP がVivado HLS 2014.4 の見積値で 250MHz 動作が可能だった。これを実機で検証してみよう。

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化5(tu1978さんのCソースコードを実機で検証)”で使用したVivado 2014.4 のプロジェクトをコピーして、フォルダ名を V_ZYBO_CAMDfL144_org とした。
このプロジェクトのラプラシアンフィルタIPを、PIPELINEディレクティブに依って高速化したラプラシアンフィルタIP に入れ替えた。
入れ替えると、IPがアップデートされなくなったので、IPを消去して、もう一度、ラプラシアンフィルタIP をAdd IP した。その際にアドレスが振られていないので、そのままインプリメントして実機で検証すると、アドレスが振られていないので、バスエラーとなった。必ず、Address Editor画面は見る習慣を付けたい。なお、ブロック・デザインで、Validate Design を行えばワーニングかエラーが出たと思う。問題解決にはhiratch さんにお世話になりました。ありがとうございました。

さて、PSにFCLK_CLK3 を追加して、FCLK_CLK3 の出力周波数を 250 MHz に設定した。

lap_filter_axim_0 のクロックと、processing_system_7_0_axi_periph (AXI Interconnect) のM03_ACLK 、 axi_mem_intercon_1 (AXI Interconnect) の S00_ACLK, S01_ACLK を FCLK_CLK3 に接続した。

ブロック・デザインを下に示す。
lap_fil_hls_14_4_80_150404.png

これで、論理合成、インプリメント、ビットストリームの生成を行ったが、タイミングでエラーが出てしまった。
lap_fil_hls_14_4_68_150402.png

詳しくタイミングを見てみた。
lap_fil_hls_14_4_69_150402.png

clk_fpga_3 (250MHzクロック)のセットアップ時間が 2 ns くらいのタイミング・エラーになっている。

lap_fil_hls_14_4_70_150402.png

clk_fpga_0 to clk_fpga_3 もセットアップ時間のエラーになっている。こちらはタイミング制約を無視するように設定しよう。

clk_fpga_0 to clk_fpga_3 に False Path を設定する。
lap_fil_hls_14_4_71_150402.png

False Path を設定できた。
lap_fil_hls_14_4_72_150402.png

250MHzの動作周波数では、clk_fpga_3 のセットアップ時間も満たしていなかったので、今度は、PS のFCLK_CLK3 を 200MHz に設定してみた。
lap_fil_hls_14_4_78_150403.png

論理合成、インプリメント、ビットストリームの生成を行ったが、やはりタイミングでエラーが出てしまった。
lap_fil_hls_14_4_79_150404.png

次にPS のFCLK_CLK3 を 166.66667MHz に設定してみた。
lap_fil_hls_14_4_73_150402.png

論理合成、インプリメント、ビットストリームの生成を行った。今度は成功した。
lap_fil_hls_14_4_74_150403.png

ハードウェアをエクスポートして、SDKを立ちあげた。

BOOT.bin を生成した。

BOOT.bin を Micro SDカードに入れて、ZYBOに挿入して電源ONした。

./cam_disp_uio コマンドでカメラ画像を表示させた。

./lap_fil_hls_1shot コマンドでラプラシアンフィルタ処理を行った。下の結果はバグが有る状態での結果です。
lap_fil_hls_14_4_75_150404.png

設定も含めたラプラシアンフィルタ全体の処理時間が 119.8 ms、ラプラシアンフィルタ処理のみの時間が 104.4 ms だった。

(2015/04/13:追記)設定も含めたラプラシアンフィルタ全体の処理時間が 78.4 ms、ラプラシアンフィルタ処理のみの時間が 63.0 ms だった。
lap_fil_hls_14_4_122_150413.png

100MHz動作周波数の時の設定も含めたラプラシアンフィルタ全体の処理時間は、約 71.4 ms だったので、かなり遅くなっている。少し遅くなっている。

シミュレーション結果のDMA Write の間隔は、169.8 us だった。

ラプラシアンフィルタ処理のみの時間を計算してみよう。

169.8 us /1.6666667 * 600 行 ≒ 61.1 ms

となった。
実機で計測した 104.4 ms との間に差があるので、何らかの原因があるのかもしれない?

計算値と実測値がだいたい合っている。
  1. 2015年04月04日 05:53 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化6(動作周波数のチューンナップ)

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化4(tu1978さんのCソースコード)
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化5(tu1978さんのCソースコードを実機で検証)
で、tu1978さんが教えてくれたCソースコードを元に、少しCソースコードをチューンナップしてみた。
この、ラプラシアンフィルタIP は、ラプラシアンフィルタ処理時間のみを計測してみたところ、約 20.0 ms だったので、60fps の16.7 ms までもう少しのところだ。そこで、動作周波数を上げて 16.7 ms を目指すことにした。
現在の動作周波数は 100MHz なので、 周期は 10 ns x (16.7 / 20.0) = 8.35 ns になれば良いことになる。動作周波数は約120 MHz となる。

ここで、 tu1978 さんの高速化ラプラシアンフィルタIP の遅延時間を 8 ns に設定してみた。
やり方は、Soultion メニューのSolution Settings... を選択して、表示されたダイアログの左側のウインドウでSynthesis を選択する。
Clock Period: を 8 に設定して、OKボタンをクリックする。
lap_fil_hls_14_4_62_150401.png

この設定で、高位合成を行うと、8 ns の制約に対して、見積もり値は、11.47 ns になってしまった。
lap_fil_hls_14_4_63_150401.png

Clock Period: を 9 に設定しても見積もり値は設定値を満足しない。
lap_fil_hls_14_4_64_150401.png

結局、7 ns とか動作周波数が速い方もいろいろと試して見たが、10 ns の制約の時が一番遅延時間が短かった。
tu1978 さんの高速化ラプラシアンフィルタIP については、動作周波数を高くすることで高速化することはできそうもない。

次に、PIPELINEディレクティブに依って高速化したラプラシアンフィルタIP の動作周波数を高くしてみよう。こちらは、tu1978 さんの高速化ラプラシアンフィルタIP ほど最適化されていない。
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化2(PIPELINEディレクティブ)
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化3(PIPELINEディレクティブを実機テスト)
を参照下さい。

このVivado HLS 2014.4 のプロジェクトで、Solution Settingsダイアログの Clock Period: を 8 に設定して、高位合成を行った。
lap_fil_hls_14_4_65_150401.png

8 ns の制約に対して、見積もり値は 7 ns で制約を満たしている。

次に、制約を 4 ns にしてみた。動作周波数は 250MHz だ。これに対しても高位合成の結果の見積もり値は 3.94 ns で制約を満たしている。
lap_fil_hls_14_4_66_150401.png

PIPELINEディレクティブを2つ追加した前回の 100 MHz 動作のの高位合成結果の内のUtilization Estimates を示す。

================================================================
== Utilization Estimates
================================================================
* Summary:
+-----------------+---------+-------+-------+-------+
| Name | BRAM_18K| DSP48E| FF | LUT |
+-----------------+---------+-------+-------+-------+
|Expression | -| 11| 0| 1080|
|FIFO | -| -| -| -|
|Instance | 0| -| 1168| 1392|
|Memory | 10| -| 0| 0|
|Multiplexer | -| -| -| 385|
|Register | -| -| 1097| 34|
+-----------------+---------+-------+-------+-------+
|Total | 10| 11| 2265| 2891|
+-----------------+---------+-------+-------+-------+
|Available | 120| 80| 35200| 17600|
+-----------------+---------+-------+-------+-------+
|Utilization (%) | 8| 13| 6| 16|
+-----------------+---------+-------+-------+-------+


PIPELINEディレクティブを2つ追加した前回の 250 MHz 動作のの高位合成結果の内のUtilization Estimates を示す。

================================================================
== Utilization Estimates
================================================================
* Summary:
+-----------------+---------+-------+-------+-------+
| Name | BRAM_18K| DSP48E| FF | LUT |
+-----------------+---------+-------+-------+-------+
|Expression | -| -| 0| 1079|
|FIFO | -| -| -| -|
|Instance | 0| 11| 1168| 1392|
|Memory | 10| -| 0| 0|
|Multiplexer | -| -| -| 431|
|Register | -| -| 1477| 50|
+-----------------+---------+-------+-------+-------+
|Total | 10| 11| 2645| 2952|
+-----------------+---------+-------+-------+-------+
|Available | 120| 80| 35200| 17600|
+-----------------+---------+-------+-------+-------+
|Utilization (%) | 8| 13| 7| 16|
+-----------------+---------+-------+-------+-------+


リソースは少し増えたくらいのようだ。

動作周波数は 2.5 倍になった。結構、これも良いかもしれない?動作周波数を高くすることができる。
レイテンシは増えていると思う。どのくらい増えているのかを確認するためにシミュレーションを行った。(制約を 4 ns にした場合、動作周波数は 250MHz )
但し、シミュレーション時のラプラシアンフィルタIP のクロックは今まで通りに 100 MHz だ。
シミュレーション結果を下に示す。
lap_fil_hls_14_4_67_150402.png

DMA Write の間隔が 169.8 us になった。以前の 100MHz 動作の場合は 91.86 us だった
ラプラシアンフィルタ処理のみの時間を計算してみよう。

169.8 us / 2.5 * 600 行 = 40752 us ≒ 40.8 ms

となった。
100MHzでのラプラシアンフィルタ処理のみの時間は、55.12 ms だったので、1.35倍速くなった。

動作周波数は2.5倍だったのに、レイテンシが増えて、実際の速度向上は 1.35倍だったのが残念だ。パイプライン化できていないところがかなりあるのだと思う。完全にパイプライン化できていれば、パイプラインのレイテンシのみが増えるだけになるだろう?
  1. 2015年04月02日 19:55 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:2