FC2カウンター FPGAの部屋 2017年09月04日

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

FPGAの部屋

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

白線追従走行用畳み込みニューラルネットワークのチューニング

白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)
白線追従走行用畳み込みニューラルネットワークの製作19(Cコードの合成、IP化)
で書いた straight_conv_nn2 プロジェクトの straight_conv_nn2.cpp をチューニングしてみた。C ソースコードはそのままに、指示子を挿入して、速度が出るようにやってみたが、結構いい加減に指示子を挿入している。

それでは、最初に今の straight_conv_nn2.cpp を貼っておく。全部貼れないので、一部だけ。それぞれの畳み込みニューラルネットワークの重みとバイアスの固定小数点数の配列は、指示子が入れられないので、straight_conv_nn2.cpp に持ってきた。そこは省略する。

int straight_conv_nn(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[ALL_PIXELS], ap_fixed<127, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT]){
#pragma HLS ARRAY_PARTITION variable=af1_weight complete dim=1
#pragma HLS ARRAY_PARTITION variable=af2_weight complete dim=1
#pragma HLS ARRAY_PARTITION variable=af2_bias complete dim=1
#pragma HLS ARRAY_PARTITION variable=af1_bias complete dim=1
#pragma HLS ARRAY_PARTITION variable=conv1_bias complete dim=1
#pragma HLS ARRAY_PARTITION variable=conv1_weight complete dim=1
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
#pragma HLS ARRAY_PARTITION variable=buf complete dim=1
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
#pragma HLS ARRAY_PARTITION variable=conv_out complete dim=1
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
#pragma HLS ARRAY_PARTITION variable=pool_out complete dim=1
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot1[100];
#pragma HLS ARRAY_PARTITION variable=dot1 complete dim=1
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];
#pragma HLS ARRAY_PARTITION variable=dot2 complete dim=1

    buf_copy1: for(int i=0; i<ROW_PIXELS; i++)
        buf_copy2: for(int j=0; j<COULMN_PIXELS; j++)
#pragma HLS PIPELINE II=1
            buf[i][j] = in[i*COULMN_PIXELS+j];

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    CONV1: for(int i=0; i<NUM_OF_KERNELS; i++){    // カーネルの個数
        CONV2: for(int j=0; j<ROW_PIXELS-4; j++){
            CONV3: for(int k=0; k<COULMN_PIXELS-4; k++){
#pragma HLS PIPELINE II=1
                conv_out[i][j][k] = 0;
                CONV4: for(int m=0; m<5; m++){
                    CONV5: for(int n=0; n<5; n++){
                        conv_out[i][j][k] += buf[j+m][k+n] * conv1_weight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_bias[i];

                if(conv_out[i][j][k]<0)    // ReLU
                    conv_out[i][j][k] = 0;
            }
        }
    }

    // Pooling Kernel = 2 x 2, Stride = 2
    POOL1: for(int i=0; i<NUM_OF_KERNELS; i++){
        POOL2: for(int j=0; j<ROW_PIXELS-4; j += 2){
            POOL3: for(int k=0; k<COULMN_PIXELS-4; k += 2){
#pragma HLS PIPELINE II=1
                POOL4: for(int m=0; m<2; m++){
                    POOL5: for(int n=0; n<2; n++){
                        if(m==0 && n==0){
                            pool_out[i][j/2][k/2] = conv_out[i][j][k];
                        } else if(pool_out[i][j/2][k/2] < conv_out[i][j+m][k+n]){
                            pool_out[i][j/2][k/2] = conv_out[i][j+m][k+n];
                        }
                    }
                }
            }
        }
    }

    af1_dot1: for(int col=0; col<100; col++){
        dot1[col] = 0;
        af1_dot2: for(int i=0; i<NUM_OF_KERNELS; i++){
            af1_dot3: for(int j=0; j<(ROW_PIXELS-4)/2; j++){
                af1_dot4: for(int k=0; k<(COULMN_PIXELS-4)/2; k++){
#pragma HLS PIPELINE II=1
                    dot1[col] += pool_out[i][j][k]*af1_weight[i*((ROW_PIXELS-4)/2)*((COULMN_PIXELS-4)/2)+j*((COULMN_PIXELS-4)/2)+k][col];
                }
            }
        }
        dot1[col] += af1_bias[col];

        if(dot1[col] < 0)    // ReLU
            dot1[col] = 0;
    }

    af2_dot1: for(int col=0; col<NUM_OF_OUTPUT; col++){
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<100; row++){
#pragma HLS PIPELINE II=1
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];

        out[col] = dot2[col];
    }

    return(0);
}


Windows 10 Pro 上の Vivado HLS 2016.4 のstraight_conv_nn2_test プロジェクトを示す。
wlt_cnn_128_170904.png

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

Estimated は 11.89 ns で 10 ns をオーバーしている。
Latency は 26924 クロックだった。100 MHz のクロックを使用すると 269.24 us で処理できる。
その代わり、リソース使用量は、BRAM_18K は 4 個、DSP48E が 27 個、FF が 9909 個で 28 %、LUT は 15033 個で 85 % を消費している。

Export RTL の結果を見てみよう。
wlt_cnn_130_170904.png

SLiCE は 3555 個、LUT 10845 個、FF 9040 個、DSP 5 個、BRAM 4 個だった。
CP achieved post-implementation の結果は 12.007 ns だった。
  1. 2017年09月04日 05:08 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0