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

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

FPGAの部屋

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

MNISTのCNN 用DMA IP 2(C コードの合成、C/RTL 協調シミュレーション、Export RTL)

MNISTのCNN 用DMA IP 1(C シミュレーション)”の続き。

前回は、MNISTのCNN 用のDMA IP を作成し、C シミュレーションを行った。今回は、C コードの合成、C/RTL 協調シミュレーション、Export RTLを行った。

まずは、Vivado HLS 2018.2 の新機能の Schedule Viewer の機能を見るために、PIPELINE指示子をコメントアウトして、C コードの合成を行った。
mnist_square_dma_7_180621.png

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

Latency は 3389 クロックだった。column_count ループの Iteration Latency が 4 クロックだったので、それでクロック数が増えているようだ。
リソース使用量は、BRAM_18K が 2 個、DSP48E が 2 個、FF が 830 個、LUT が 1270 個だった。

Analysis ボタンをクリックして、Schedule Viewer を表示させた。
mnist_square_dma_9_180621.png

row_count と column_count があるのが分かる。
Operation\Control Step の数は 12 だった。

次に、PIPELINE指示子を戻した。
mnist_square_dma_10_180621.png

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

Latency は 3389 クロックから 809 クロックに下がった。
リソース使用量は、BRAM_18K が 2 個、DSP48E が 3 個、FF が 1378 個、LUT が 1583 個だった。PIPELINE指示子が無い時よりも大分増えている。

Analysis ボタンをクリックして、Schedule Viewer を表示させた。
mnist_square_dma_12_180621.png

row_count と column_count が合わさって、row_count_column_count になっていた。
Operation\Control Step の数は 25 だった。

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

Latency は 1014 クロックだった。

C/RTL 協調シミュレーションの波形を示す。
まずは波形全体から。
mnist_square_dma_14_180621.png

AXI4-Stream の TLAST をアサートしている辺りで、TVALID が 0 にディアサートされているのが気になるが、スループットは出ているようだ。

次に最初の部分を拡大してみよう。
mnist_square_dma_15_180621.png

28 個のピクセルの DMA を行う。ARLENを見ると最初は 0x0f なので 16 バーストで、次は 0x0B なので 12 バーストの計 28 個バーストしている。
アドレス転送とデータ転送がもう少しオーバーラップしている方が良いと思う。

Export RTLを行った。
Vivado HLS 2018.2 では、Wave Debug 項目が増えている。これは、Verilog/VHDL Simulator Selection が AUTO だとハイドされている。これが有効になるのは、Vivado Simulator の時だけのようだ。このオプションにチェックすると、C/RTL 協調シミュレーションが終了する前にVivado が起動して、波形を表示する。
mnist_square_dma_16_180621.png

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

LUT が 575 個、FF が 807 個、DSP が 4 個、BRAM が 1 個、SRLが 39 個使用していた。
  1. 2018年06月22日 04:51 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

MNISTのCNN 用DMA IP 1(C シミュレーション)

MNISTのCNN 用のDMA IP を作成した。
MNISTのCNN は、ap_uint<8> のHLS ストリーム・データを入力する。よって、MNISTのCNN に手書き数字データを供給するために画像を切り取り 28 x 28 ピクセルのHLSストリーム・データを供給する mnist_square_dma IP を作成する。

Vivado HLS 2018.2 の mnist_square_dma プロジェクトを示す。
mnist_square_dma_1_180621.png

mnist_square_dma.cpp を示す。

// mnist_square_dma.cpp
// 2018/06/19 by marsee
//

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

int conv_rgb2y(int rgb);

int mnist_square_dma(int in[22400], int addr_offset, hls::stream<ap_axiu<8,1,1,1> >&outs){
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=22400 port=in offset=slave
#pragma HLS INTERFACE s_axilite port=return

    ap_axiu<8,1,1,1> pixel;

    row_count: for(int i=0; i<28; i++){
        column_count: for(int j=0; j<28; j++){
#pragma HLS PIPELINE II=1
            pixel.data = 255 - conv_rgb2y(in[addr_offset+i*800+j]);
            // 白地に黒数字から黒地に白数字に変換

            if(i==0 && j==0)
                pixel.user = 1;
            else
                pixel.user = 0;
            if(j == 27)
                pixel.last = 1;
            else
                pixel.last = 0;

            outs << pixel;
        }
    }
    return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    if (y >= 256)
        y = 255;

    return(y);
}


mnist_square_dma_tb.cpp を示す。

// mnist_square_dma_tb.cpp
// 2018/06/21 by marsee
//

#include <stdio.h>
#include <ap_fixed.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "bmp_header.h"

#define READ_BMP_FILE_NAME    "bmp_file0.bmp"

// 8
#define X_POS    560
#define Y_POS    183
// 7
//#define X_POS    504
//#define Y_POS    184
// 5
//#define X_POS    390
//#define Y_POS    138
// 0
//#define X_POS    390
//#define Y_POS    70
#define WIDTH    28
#define HEIGHT    28

int mnist_square_dma(int in[22400], int addr_offset, hls::stream<ap_axiu<8,1,1,1> >&outs);

int main(){
    int *in;
    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr;
    int *rd_bmp;
    int blue, green, red;
    hls::stream<ap_axiu<8,1,1,1> > outs;
    ap_axiu<8,1,1,1> pixel;

    if ((fbmpr = fopen(READ_BMP_FILE_NAME, "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open ");
        fprintf(stderr, READ_BMP_FILE_NAME);
        fprintf(stderr, " by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    // ピクセルを入れるメモリをアロケートする
    if ((rd_bmp =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }

    if ((in =(int *)malloc(sizeof(int) * (800 * 28))) == NULL){
        fprintf(stderr, "Can't allocate (ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT>)in memory\n");
        exit(1);
    }

    // rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr);
            green = fgetc(fbmpr);
            red = fgetc(fbmpr);
            rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr);

    // rd_bmp を in に入力
    for (int y=Y_POS; y<Y_POS+HEIGHT; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            in[(y-Y_POS)*bmpihr.biWidth+x] = rd_bmp[y*bmpihr.biWidth+x];
        }
    }

    mnist_square_dma(in, X_POS, outs);

    for (int y=0; y<HEIGHT; y++){
        for (int x=0; x<WIDTH; x++){
            outs >> pixel;
            if(pixel.data >= 0x80)
                printf("*");
            else
                printf(" ");
        }
        printf("\n");
    }

    free(rd_bmp);
    free(in);

    return(0);
}


mnist_square_dma_tb.cpp で 下の画像の手書き数字を切り取る。
mnist_square_dma_3_180621.jpg

現在の mnist_square_dma_tb.cpp は 8 を切り取るようになっている。C シミュレーションをしてみよう。
mnist_square_dma_2_180621.png

ご覧の通りに 8 が切り取られている。

7 の切り取り。
mnist_square_dma_4_180621.png

5 の切り取り。
mnist_square_dma_5_180621.png

0 の切り取り。
mnist_square_dma_6_180621.png
  1. 2018年06月21日 05:29 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS の任意精度固定小数点データ型の飽和演算

Kerasを勉強して、Keras でMNISTのCNNのパラメータを抽出して、Vivado HLS で自作テンプレートを使ってCNN を実装しているが、現在の自作テンプレートは飽和演算をサポートしていない。これは、飽和演算をサポートするよりは、演算によって取りうる値域すべてをカバーする演算用のビット長を与えたほうが、リソース使用量が小さいという実証データに基づいていた。ただし、特に、全結合層に言えることだが、演算結果の値域をカバーするような演算ビット幅を与えても、途中の計算でオーバーフローしてしまう可能性がある。つまり、整数部4ビットでは、+7.XXX ~ -8.XXX の数を表せるが、最終的に +4.XXX でもそれは、+36.XXX から +32.XXX を引いた結果かもしれない?その場合には、整数部4ビットで演算させる場合は、途中の演算で破綻している訳である。それでもたま~にであれば、それも量子化による誤差の一部として認めても良いと思っている。あくまでもリソース使用量との兼ね合いで。。。(ちなみに、Vivado HLSでは自由にビット長を選べるので、各層ごとに演算ビット長を変更している)
今度、飽和演算をサポートしたCNN とサポートしていないCNN の精度の差を比べてみようと思っている。

さて、全結合層は演算自体が多いので、演算自体に飽和演算をサポートするが、畳み込み層は1回の演算数が今のところ25個なので、重みの最大値、最小値から演算の途中の最大値を導出することができる。よって、正しい値になるような演算ビット長を推測しやすい。そのような演算ビット長で演算はするのだが、次の層に送る場合に飽和演算をサポートするとよりリソース使用量が削減される可能性がある。そのような演算をサポートするために飽和演算をサポートしない演算の結果を飽和演算しながら、丸めたい。
と言う訳で今日のブログにつながる訳である。

さて、まずは、sat_test1.h を示す。

// sat_test1.h
// 2018/06/07 by marsee
//

#ifndef __SAT_TEST1_H__
#define __SAT_TEST1_H__

#include <ap_fixed.h>

typedef ap_fixed<53, AP_TRN, AP_WRAP> ap_fixed_inout_def;
typedef ap_fixed<53, AP_TRN, AP_SAT> ap_fixed_sat_def; // 飽和演算
typedef ap_fixed<75, AP_TRN, AP_WRAP> ap_fixed_mid_def;

#endif


次に、sat_test1.cpp を示す。合成する気はさらさらないので、途中で printf() を使用している。

// sat_test1.cpp
// 2018/06/07 by marsee
//

#include "sat_test1.h"

int sat_test1(ap_fixed_inout_def in0, ap_fixed_inout_def in1,
        ap_fixed_inout_def &out){
    ap_fixed_mid_def calc;

    calc = ap_fixed_mid_def(in0) * ap_fixed_mid_def(in1);
    printf("calc = %f\n"float(calc));
    out = ap_fixed_inout_def(ap_fixed_sat_def(calc));

    return(0);
}


最後にテストベンチの sat_test1_tb.cpp を示す。

// sat_test1_tb.cpp
// 2018/06/07 by marsee
//

#include "sat_test1.h"

int sat_test1(ap_fixed_inout_def in0, ap_fixed_inout_def in1,
        ap_fixed_inout_def &out);

int main(){
    ap_fixed_inout_def in0_0, in1_0;
    ap_fixed_inout_def in0_1, in1_1;
    ap_fixed_inout_def out0, out1;

    in0_0 = 1.5;
    in1_0 = 1.5;
    sat_test1(in0_0, in1_0, out0);
    printf("in0_0 = %f, in1_0 = %f, out = %f\n",
        float(in0_0), float(in1_0), float(out0));

    in0_1 = 3.0;
    in1_1 = 3.0;
    sat_test1(in0_1, in1_1, out1);
    printf("in0_1 = %f, in1_1 = %f, out = %f\n",
        float(in0_1), float(in1_1), float(out1));

    in0_1 = -3.0;
    in1_1 = 3.0;
    sat_test1(in0_1, in1_1, out1);
    printf("in0_1 = %f, in1_1 = %f, out = %f\n",
        float(in0_1), float(in1_1), float(out1));

    return(0);
}


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

1.5 x 1.5 = 2.25 は演算後のビット長が定義したビット長に収まっているので、これは正しい。
3.0 x 3.0 = 3.75 は途中の結果は 9.0 だけど、飽和演算して、3.75 に丸められているので、問題無い様だ。
-3.0 x 3.0 = -4.00 は途中の結果は -9.0 だけど、飽和演算して、-4.00 に丸められているので、問題無い様だ。
  1. 2018年06月08日 04:35 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

今年度のVivado HLSセミナ

今年度の筑波大学の学生さん向けのVivado HLSセミナは5月30日(水)からで、全10回の予定です。約2時間から3時間です。
昨年度、AXI4 Masterから急に難しくなったという意見を汲んで、初心者編6回と応用編4回の計10回にしました。
メニューです。

・Vivado HLS 初心者編
1. 第 1 回目(2 時間程度)No1 のフォルダに資料があります。実習はありません
 (ア) Zynq の概要
 (イ) AXI インターフェース
 (ウ) Vivado HLS(概要)
2. 第 2 回目(半日程度)資料-No2 フォルダ
 (ア) Vivado HLS 勉強会 1(基礎編)(ZYBO 実機を使用します)
  ① Vivado HLS の基本的に使い方を学習します。簡単な乗算回路の IP を作ります。組み合わせ回路を作ります。
  ② Vivado HLS で作った IP を使用して、Vivado で IPI を使って乗算回路にして ZYBOで実際に動作させます。
3. 第 3 回目(半日程度)資料-No3 フォルダ
 (ア) Vivado HLS 勉強会 2(レジスタの挿入と PIPELINE ディテクティブ)
  ① 第 2 回目で作成した乗算回路にレジスタを挿入します。
  ② パイプライン処理の方法を学習します。
  ③ Vivado HLS でディスプレイ・コントローラを作ってみます。
4. 第 4 回(半日程度)資料-No4 フォルダ(ZYBO 実機を使用します)
 (ア) Vivado HLS 勉強会 3(AXI4 Lite Slave)
  ① 乗算回路を AXI4 Lite Slave インターフェース対応にします。
  ② Vivado HLS で作った乗算回路 IP を Vivado の IP インテグレータ―にインスタンスし、ZYBO で実際に動作させます。
5. 第 5 回(半日程度)資料-No5n フォルダ
 (ア) Vivado HLS 勉強会 4(AXI4 Master 初心者編)
  ① 2 乗回路(簡単な例)を題材に AXI4 Master の実装方法を学習します。
  ② Vivado HLS で 2 乗回路 IP を作成し、Vivado で実装して ZYBO 実機で 2 乗結果を確認します(AXI_HP ポート使用)。
6. 第 6 回(半日程度)資料-No6n フォルダ
 (ア) Vivado HLS 勉強会 5(AXI4 Stream 初心者編)
  ① 第 5 回と同じ 2 乗回路(簡単な例)を題材に AXI4-Stream の実装方法を学習します。
  ② Vivado HLSで2乗回路IPを作成し、DMA IPと組み合わせてVivadoで実装してZYBO実機で 2 乗結果を確認します(AXI_ACP ポート使用)。

・Vivado HLS 応用編
7. 第 7 回(半日程度)資料-No5 フォルダ
 (ア) Vivado HLS 勉強会 4(AXI4 Master)
  ① ラプラシアンフィルタを題材に AXI4 Master の実装方法を学習します。
  ② ディレクティブ(指示子)や C の構造による性能の違いを学習します(チューニング方法)。
8. 第 8 回(半日程度)資料-No6 フォルダ
 (ア) Vivado HLS 勉強会 5(AXI4 Stream)+HLS ビデオライブラリ
  ① 第 5 回目のラプラシアンフィルタを AXI4 Stream で実装します。AXI4 Stream は一番性能が出やすくお得です。
  ② HLS ビデオライブラリについて説明します。講義のみです。
9. 第 9 回(半日程度)資料-No7 フォルダ(ZYBO 実機を使用します)
 (ア) Vivado HLS の IP を Linux から使用する
  ① 第 4 回目で作成した乗算回路を Linux から使用する方法を学習します。
  ② SDK で BOOT.bin を作成し、devicetree や uImage とともに SD カードに書き込みます。
  ③ SD カードの第 2 パーティションには私の Ubuntu 14.04 の RootFS を入れておいて、ZYBO 上で Ubuntu を起動します。
  ④ Vivado HLS のドライバを使って乗算回路 IP をソフトで制御するために Makefile を作ってコンパイル実行します。
  ⑤ Linux の CMA 領域を使用する udmabuf について学習します。
10. 第 10 回(半日程度)資料-No8 フォルダ
 (ア) 任意精度固定小数点データ型
  ① いろいろな任意精度固定小数点データ型に対して量子化モード、オーバーフローモードを変更して C シミュレーションし、その違いを実感する
  ② また、①の C コードの合成を行い、リソース使用量の違いを確認する。
 (イ) Vivado HLS のビデオ関数
  ① Vivado HLS の OpenCV 対応ライブラリについて学習する
  ② Vivado HLS のビデオ関数を使用した Sobel フィルタについて実習する。
  ③ Vivado HLS のビデオ関数を使用した FASTX コーナー検出について実習する。
  1. 2018年05月22日 08:33 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

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

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

Vivado HLSでのAXI4-Stream のテンプレートを作成する1”で、畳み込み層の特徴マップの個数に対応するために、データを配列にしたのだが、AXI4-Stream にするという制約ではうまくかなかった。よって、”Vivado HLSでのAXI4-Stream のテンプレートを作成する2”でデータを展開して書いたのだけれど、これでは不満が残った。たくさん特徴マップがあった時に書くのが大変だからだ。それに、特徴マップの数を変更することが難しい。配列で書けば配列の数を書き換えると、すぐに特徴マップの数を変更することができる。
その後、AXI4-Stream で各層をつなぐのではなく、HLS ストリームで接続することにしたので、データを配列にできると思う。今日はそのテストをしてみようと思う。

Vivado HLSでのAXI4-Stream のテンプレートを作成する1”の stream_test.cpp を書き換えた。stream_test.cpp を示す。

// stream_test.cpp
// 2018/02/11 by marsee
// 2018/04/12 : DATA_PACK指示子を追加
//

#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, int num){
#pragma HLS DATA_PACK variable=outs
#pragma HLS DATA_PACK variable=ins

    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++){
#pragma HLS PIPELINE II=1
            ins >> ins_t;
            for(int i=0; i<num; i++){
                outs_t.data[i] = ins_t.data[i] * (ap_fixed<16, 6, AP_TRN, AP_WRAP>)(i+2);
            }
            outs_t.user = 1;
            outs_t.last = 0;

            outs << outs_t;
        }
    }

    return(0);
}

int stream_top(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 DATA_PACK variable=outs
#pragma HLS DATA_PACK variable=ins
#pragma HLS DATAFLOW

    hls::stream<ap_fixed_axis<16,6,2,1,1,1> > temp_st;

    stream_test(ins, temp_st, 2);
    stream_test(temp_st, outs, 2);

    return(0);
}


新たに stream_top() 関数を作成して stream_test() を 2 回読んでいる。これは、HLS ストリームの層を複数呼んだことになる。
stream_top() の入力ストリーム、出力ストリームに data_pack 指示子を付けたら、呼ばれた stream_test() の方にも入力ストリーム、出力ストリームに DATA_PACK 指示子を付けないとエラーになった。(stream_top() にだけ DATA_PACK 指示子を付けたらエラーになった)

stream_top.h は ”Vivado HLSでのAXI4-Stream のテンプレートを作成する1”のコードをそのまま使用している。

stream_test プロジェクトを示す。
stream_test2_1_180412.png

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

Estmated は 6.01 ns で十分な性能が出ている。
Latency は 565 クロックで、総ピクセル数が 560 ピクセルなので、ほぼ 1 クロックで 1 ピクセルの処理ができている。

DATA_PACK 指示子を付けた状態でのVHDL の entity 宣言部分を示す。

entity stream_top is
port (
    ins_V_dout : IN STD_LOGIC_VECTOR (39 downto 0);
    ins_V_empty_n : IN STD_LOGIC;
    ins_V_read : OUT STD_LOGIC;
    outs_V_din : OUT STD_LOGIC_VECTOR (39 downto 0);
    outs_V_full_n : IN STD_LOGIC;
    outs_V_write : OUT STD_LOGIC;
    ap_clk : IN STD_LOGIC;
    ap_rst : IN STD_LOGIC;
    ap_done : OUT STD_LOGIC;
    ap_start : IN STD_LOGIC;
    ap_ready : OUT STD_LOGIC;
    ap_idle : OUT STD_LOGIC;
    ap_return : OUT STD_LOGIC_VECTOR (31 downto 0) );
end;


hls::stream<ap_fixed_axis<16,6,2,1,1,1> > がパックされているのがわかる。

DATA_PACK 指示子を取り除いた状態でのVHDL の entity 宣言部分を示す。

entity stream_top is
port (
    ins_V_data_0_V_dout : IN STD_LOGIC_VECTOR (15 downto 0);
    ins_V_data_0_V_empty_n : IN STD_LOGIC;
    ins_V_data_0_V_read : OUT STD_LOGIC;
    ins_V_data_1_V_dout : IN STD_LOGIC_VECTOR (15 downto 0);
    ins_V_data_1_V_empty_n : IN STD_LOGIC;
    ins_V_data_1_V_read : OUT STD_LOGIC;
    ins_V_keep_V_dout : IN STD_LOGIC_VECTOR (1 downto 0);
    ins_V_keep_V_empty_n : IN STD_LOGIC;
    ins_V_keep_V_read : OUT STD_LOGIC;
    ins_V_strb_V_dout : IN STD_LOGIC_VECTOR (1 downto 0);
    ins_V_strb_V_empty_n : IN STD_LOGIC;
    ins_V_strb_V_read : OUT STD_LOGIC;
    ins_V_user_V_dout : IN STD_LOGIC_VECTOR (0 downto 0);
    ins_V_user_V_empty_n : IN STD_LOGIC;
    ins_V_user_V_read : OUT STD_LOGIC;
    ins_V_last_V_dout : IN STD_LOGIC_VECTOR (0 downto 0);
    ins_V_last_V_empty_n : IN STD_LOGIC;
    ins_V_last_V_read : OUT STD_LOGIC;
    ins_V_id_V_dout : IN STD_LOGIC_VECTOR (0 downto 0);
    ins_V_id_V_empty_n : IN STD_LOGIC;
    ins_V_id_V_read : OUT STD_LOGIC;
    ins_V_dest_V_dout : IN STD_LOGIC_VECTOR (0 downto 0);
    ins_V_dest_V_empty_n : IN STD_LOGIC;
    ins_V_dest_V_read : OUT STD_LOGIC;
    outs_V_data_0_V_din : OUT STD_LOGIC_VECTOR (15 downto 0);
    outs_V_data_0_V_full_n : IN STD_LOGIC;
    outs_V_data_0_V_write : OUT STD_LOGIC;
    outs_V_data_1_V_din : OUT STD_LOGIC_VECTOR (15 downto 0);
    outs_V_data_1_V_full_n : IN STD_LOGIC;
    outs_V_data_1_V_write : OUT STD_LOGIC;
    outs_V_keep_V_din : OUT STD_LOGIC_VECTOR (1 downto 0);
    outs_V_keep_V_full_n : IN STD_LOGIC;
    outs_V_keep_V_write : OUT STD_LOGIC;
    outs_V_strb_V_din : OUT STD_LOGIC_VECTOR (1 downto 0);
    outs_V_strb_V_full_n : IN STD_LOGIC;
    outs_V_strb_V_write : OUT STD_LOGIC;
    outs_V_user_V_din : OUT STD_LOGIC_VECTOR (0 downto 0);
    outs_V_user_V_full_n : IN STD_LOGIC;
    outs_V_user_V_write : OUT STD_LOGIC;
    outs_V_last_V_din : OUT STD_LOGIC_VECTOR (0 downto 0);
    outs_V_last_V_full_n : IN STD_LOGIC;
    outs_V_last_V_write : OUT STD_LOGIC;
    outs_V_id_V_din : OUT STD_LOGIC_VECTOR (0 downto 0);
    outs_V_id_V_full_n : IN STD_LOGIC;
    outs_V_id_V_write : OUT STD_LOGIC;
    outs_V_dest_V_din : OUT STD_LOGIC_VECTOR (0 downto 0);
    outs_V_dest_V_full_n : IN STD_LOGIC;
    outs_V_dest_V_write : OUT STD_LOGIC;
    ap_clk : IN STD_LOGIC;
    ap_rst : IN STD_LOGIC;
    ap_done : OUT STD_LOGIC;
    ap_start : IN STD_LOGIC;
    ap_ready : OUT STD_LOGIC;
    ap_idle : OUT STD_LOGIC;
    ap_return : OUT STD_LOGIC_VECTOR (31 downto 0) );
end;


入力ポートごとに empty_n, read ポートがついて、出力ポートごとに full_n, write ポートが生成されている。これは無駄だ。

これから、内部で使用する HLS ストリームのポートに関しては、DATA_PACK 指示子を付けたほうが良さそうだ。付けなくても問題ないかもしれないが。。。
  1. 2018年04月12日 04:45 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS のエラー内容がGUI に表示されていない時の対処方法

Vivado HLS のC コードの合成で、GUI にエラー内容が表示されずに、何のエラーで終了したのかが分からない時があって困っていた。
例えば、全結合層の実装しているときのエラーを示す。
affine_layer_1_180227.png

エラー内容を貼っておく。

Failed checking during preprocessing.
    while executing
"source C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO_Z7-20/test/affine_layer1/solution1/csynth.tcl"
    invoked from within
"hls::main C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO_Z7-20/test/affine_layer1/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.


どういうエラーだかが全く分からない。。。

そういう時は、solution1 の solution1.log (solution2 でやっているときはsolution2 と読み替えること)に書いてあった。
affine_layer_2_180227.png

solution1.log を示す。
affine_layer_3_180227.png

affine_layer1/affine_layer1.cpp:38:24: error: no viable overloaded '='
              dot[col] = 0;

affine_layer1/affine_layer1.cpp:40:23: error: no viable overloaded '+='
             dot[col] += stdata.data.data0 * af1_weight[y*26 +x][col];


エラー内容が分かった。キャストなどのコードを追加しよう。

と思ったのだが、エラーの原因は配列のdot[100] を間違って、mdata_type で宣言してしまったことだった。
mdata_type は次の様に宣言してある。

typedef struct {
    ap_fixed<19,7,AP_TRN,AP_WRAP> data [NUMBER_OF_MIDDLE_LAYER];
} mdata_type;


よってC++ のコードが間違っている。この mdata_type を使うのは間違いなので、次の affine_type を使うように書き直した。

typedef ap_fixed<19,7,AP_TRN,AP_WRAP> affine_type;


これでエラーが無くなった。

しかし、普通にVivado HLS のGUI 上にエラー内容を表示してほしいものである。。。
  1. 2018年02月27日 04:16 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream インターフェースの畳み込み層4(C/RTL 協調シミュレーションとExport RTL)

AXI4-Stream インターフェースの畳み込み層3(C ソースコード)”の続き。

前回は、すべてのC ソースコードとBMP 画像ファイルを貼ったので、手元でも確認できる様になったと思う。今回は、畳み込み層のC/RTL 協調シミュレーションとExport RTLを行う。

さて、早速 C/RTL 協調シミュレーションを行った。結果を示す。
conv_layer_13_180215.png
Latency は 604 クロックだった。

C/RTL 協調シミュレーション波形を示す。
まずは全体波形から。
conv_layer_14_180215.png

入力はins_TVALID はずっと 1 のままで、ins_TREADY は最初に少し 0 になるときもあるが、その後は 1 のままとなっていて、スループットが取れていることが分かる。
out_TVALID が時々 0 になるのは、行の初めで出力できない、列のインデックスが 3 以下の時だと思う。その後は、ずっと 1 なので問題ないだろう。

AXI4 Lite Slave インターフェース部分の波形を示す。
conv_layer_15_180215.png

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

C コードの合成結果と比べると、DSP48E の 23 個は変化が無いが、FF は合成時に 2745 個だったのが、Export RTL では 962 個に減少した。LUT は 2786 個だったのが、635 個に減少した。
  1. 2018年02月16日 05:14 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0
»