FC2カウンター FPGAの部屋 DNN

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

FPGAの部屋

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

AXI4-Stream インターフェースの畳み込み層1(C コードの合成)

Vivado HLSでのAXI4-Stream のテンプレートを作成する2”でAXI4-Stream インターフェースのテンプレートが決定できたので、いよいよ畳み込み層を作ってみよう。畳み込み層は今まで作ってきた画像フィルタと何ら変わるところはない。

畳み込み層のVivado HLS 2017.4 プロジェクトの conv_layer プロジェクトを示す。
だけど、まだテストベンチが完成していないので、ハードウェア化する conv_layter() 関数が正しいかどうか?はまだ分からない?
そして、conv_layer.cpp は 2 つの方法で作成した。1 つは、2次元配列のラインバッファと2次元配列の入力データ・ウインドウを使用した実装で、もう1つはVivado HLS のHLS ビデオライブラリの hls::LineBuffer と hls::Window を使用した実装だ。この2つの畳み込み層の実装を比べてみよう。2つともそれぞれ、ラインバッファと入力データ・ウインドウ以外は同じ実装になっている。
まずは、2次元配列のラインバッファと2次元配列の入力データ・ウインドウを使用した実装から見てみる。
conv_layer_1_180214.png

共通に使用する conv_layer.h から示す。

// conv_layter.h
// 2018/02/06 by marsee
//

#ifndef __CONV_LAYER_H__
#define __CONV_LAYER_H__
#include <ap_fixed.h>

template<int W, int I, int U, int TI, int TD>
    struct ap_fixed1_axis{
        struct data {
            ap_fixed<W,I,AP_TRN,AP_WRAP> data0;
        } data;
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int W, int I, int U, int TI, int TD>
    struct ap_fixed2_axis{
        struct data {
            ap_fixed<W,I,AP_TRN,AP_WRAP> data0;
            ap_fixed<W,I,AP_TRN,AP_WRAP> data1;
        } data;
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int U, int TI, int TD>
    struct float2_axis{
        struct data {
            float data0;
            float data1;
        } data;
        ap_uint<1> keep;
        ap_uint<1> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

#define HORIZONTAL_PIXEL_WIDTH  56
#define VERTICAL_PIXEL_WIDTH    10

#define ARRAY_SIZE                5

#define NUMBER_OF_KERNEL        2

typedef ap_ufixed<80, AP_TRN, AP_WRAP> in_type;
typedef ap_fixed<226, AP_TRN, AP_WRAP> val_type;
typedef ap_fixed<166, AP_TRN, AP_WRAP> out_type;

#endif


2次元配列のラインバッファと2次元配列の入力データ・ウインドウを使用した実装の conv_layer.cpp を示す。

// conv_layer.cpp (line_buf, pix_mat)
// 2018/02/06 by marsee
//

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

#include "conv_layer.h"
#include "conv1_weight.h"
#include "conv1_bias.h"

int conv_layer(hls::stream<ap_axiu<32,1,1,1> >& ins,
        hls::stream<ap_fixed2_axis<16,6,1,1,1> >& outs){
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axiu<32,1,1,1> pix;
    ap_fixed2_axis<16,6,1,1,1> conv_out;

    in_type line_buf[ARRAY_SIZE-1][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS ARRAY_PARTITION variable=line_buf block factor=4 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P

    in_type pix_mat[ARRAY_SIZE][ARRAY_SIZE];
#pragma HLS array_partition variable=pix_mat complete

    in_type ap_uf_pix;
    val_type val;

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

    Loop2: for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        Loop3: for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            ap_uf_pix = (in_type)((ap_ufixed<168, AP_TRN, AP_WRAP>)(pix.data & 0xff) / 256);

            // 2次元配列のデータを左シフト
            Loop4 : for (int k=0; k<ARRAY_SIZE; k++){
                Loop5 : for (int m=0; m<ARRAY_SIZE-1; m++){
#pragma HLS UNROLL
                    pix_mat[k][m] = pix_mat[k][m+1];
                }
            }

            Loop6: for (int i=0; i<ARRAY_SIZE-1; i++){ // 以前の行のデータを line_buf から入力
                pix_mat[i][ARRAY_SIZE-1] = line_buf[i][x];
            }
            pix_mat[ARRAY_SIZE-1][ARRAY_SIZE-1] = ap_uf_pix; // pix_mat の最後に新しいデータを入力

            Loop7: for (int i=0; i<ARRAY_SIZE-2; i++){ // 行の入れ替え
                line_buf[i][x] = line_buf[i+1][x];
            }
            line_buf[ARRAY_SIZE-2][x] = ap_uf_pix;

            // conv_layer の演算
            for (int k=0; k<NUMBER_OF_KERNEL; k++){
                val = 0.0;
                for (int j=0; j<ARRAY_SIZE; j++){
                    for (int i=0; i<ARRAY_SIZE; i++){
                        val += (val_type)pix_mat[j][i] * (val_type)conv1_weight[k][0][j][i];
                    }
                }
                val += (val_type)conv1_bias[k];
                if(k==0)
                    conv_out.data.data0 = val;
                else
                    conv_out.data.data1 = val;
            }


            // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので出力しない
            if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1))
                continue;
            else { // 有効なデータの時
                if (x==(ARRAY_SIZE-1) && y==(ARRAY_SIZE-1)){ // 最初のデータでは、TUSERをアサートする
                    conv_out.user = 1;
                } else {
                    conv_out.user = 0;
                }

                if (x == (HORIZONTAL_PIXEL_WIDTH-1)){    // 行の最後で TLAST をアサートする
                    conv_out.last = 1;
                } else {
                    conv_out.last = 0;
                }

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


C コードの合成を行った。結果を示す。
conv_layer_2_180214.png

Latency は 577 クロックだった。処理する画像は 560 ピクセルなので、約 1.02 クロック/ピクセルとなった。性能的には問題ないと思う。
リソース使用量は DSP48E を 23 個、FF を 2745 個、LUT を 2786 個使用している。問題ないリソース使用量だと思う。

次に、Vivado HLS のHLS ビデオライブラリの hls::LineBuffer と hls::Window を使用した実装の conv_layer.cpp を示す。
conv_layer_3_180214.png

// conv_layer.cpp (hls::LineBuffer, hls::Window)
// 2018/02/06 by marsee
//

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

#include "conv_layer.h"
#include "conv1_weight.h"
#include "conv1_bias.h"

int conv_layer(hls::stream<ap_axiu<32,1,1,1> >& ins,
        hls::stream<ap_fixed2_axis<16,6,1,1,1> >& outs){
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axiu<32,1,1,1> pix;
    ap_fixed2_axis<16,6,1,1,1> conv_out;

    hls::LineBuffer<ARRAY_SIZE-1, HORIZONTAL_PIXEL_WIDTH, in_type> linebuf;
    hls::Window<ARRAY_SIZE, ARRAY_SIZE, in_type> mbuf;

    in_type ap_uf_pix;
    val_type val;

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

    Loop1: for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        Loop2: for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            ap_uf_pix = (in_type)((ap_ufixed<168, AP_TRN, AP_WRAP>)(pix.data & 0xff) / 256);

            mbuf.shift_pixels_left();    // mbuf の列を1ビット左シフト
            for(int i=0; i<ARRAY_SIZE-1; i++){
                mbuf.insert_pixel(linebuf.getval(i,x), i, ARRAY_SIZE-1);
            }
            mbuf.insert_pixel(ap_uf_pix, ARRAY_SIZE-1, ARRAY_SIZE-1);

            // LineBuffer の更新
            linebuf.shift_pixels_up(x);
            linebuf.insert_bottom_row(ap_uf_pix, x);

            // conv_layer の演算
            for (int k=0; k<NUMBER_OF_KERNEL; k++){
                val=0.0;
                for (int j=0; j<ARRAY_SIZE; j++){
                    for (int i=0; i<ARRAY_SIZE; i++){
                        val += (val_type)mbuf.getval(j,i) * (val_type)conv1_weight[k][0][j][i];
                    }
                }
                val += (val_type)conv1_bias[k];
                if(k==0)
                    conv_out.data.data0 = val;
                else
                    conv_out.data.data1 = val;
            }


            // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので出力しない
            if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1))
                continue;
            else { // 有効なデータの時
                if (x==(ARRAY_SIZE-1) && y==(ARRAY_SIZE-1)){ // 最初のデータでは、TUSERをアサートする
                    conv_out.user = 1;
                } else {
                    conv_out.user = 0;
                }

                if (x == (HORIZONTAL_PIXEL_WIDTH-1)){    // 行の最後で TLAST をアサートする
                    conv_out.last = 1;
                } else {
                    conv_out.last = 0;
                }

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



C コードの合成を行った。結果を示す。
conv_layer_4_180214.png

なんと、前の2次元配列のラインバッファと2次元配列の入力データ・ウインドウを使用した実装と全く一緒の実装だった。。。
  1. 2018年02月14日 06:49 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

これから作る畳み込みニューラルネットワークについての目標2

これから作る畳み込みニューラルネットワークについての目標”の続き。

AXI4-Stream 対応のラプラシアンフィルタの様に畳み込みニューラルネットワークを作る予定だ。
畳み込み層はそのままフィルタなので、ラプラシアンフィルタなどの構造を元に作ることができると思う。
それに各層、畳み込み層やReLU、マックス・プーリング層、全結合層などをAXI4-Stream インターフェースで接続されるIPとしてVivado のIP インテグレータで接続すればよいのではないだろうか?もし、1 個のFPGA でロジックが足りなければ、AXI4-Stream インターフェースを外に出して他のFPGA に接続すれば良いのでは?と思う。

つまりこんなイメージだ。
AXI4-Stream_CNN_1_180207.png

ストリーム・データをIP を接続して処理させるのはFPGAの得意とするところだし、一番利点が生きる構成だ。これを使わない手は無いと思う。当然複雑なCNN はFPGAのリソースが足りなくてできないのだが、今度はストリームされる画像データ分だけの演算器があれば良いし、(1 クロックで処理できるとしてだが。。。)何とかなるだろう?

2つ目の全結合層では、1つ目の全結合層の演算がすべて終わるまで 1 個のデータも入ってこない。そこがボトルネックだが仕方がない。そので1フレームずれるのは仕方がないか。。。
  1. 2018年02月07日 21:07 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

これから作る畳み込みニューラルネットワークについての目標

2018/02/02 : ”Vivado HLS 2017.4 で片方が定数の場合の乗算の検討5(畳み込み演算2)”のExport RTL 時の結果を受けて、記事の数値を入れ替えました。

今まで、1クロックごとに1判定出力できる畳み込みニューラルネットワークを Vivado HLS で作ろうと思って、以下の記事でテストしていた。
Vivado HLS 2017.4 で片方が定数の場合の乗算の検討1(C シミュレーション)
Vivado HLS 2017.4 で片方が定数の場合の乗算の検討2(C コードの合成1)
”Vivado HLS 2017.4 で片方が定数の場合の乗算の検討3(C コードの合成2)
Vivado HLS 2017.4 で片方が定数の場合の乗算の検討4(畳み込み演算1)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討4(畳み込み演算1)”が”畳み込み演算1”と書いてあることから分かる通りにまだ後の記事がある。
しかし、劇的にロジックは減らない。
ここで計算してみると、5 x 5 の畳み込み演算に 853 LUT 使用するので、白線間走行用畳み込みニューラルネットワークで使用してる画像サイズの Row x Column = 10 x 56 ピクセルの画像は、ストライド 1 だと畳み込み演算器は 6 x 52 = 312 個必要だ。フィルタ数は 2 個なので、その倍、つまり、624 個必要となる。
つまり、624 x 853 LUT = 532,272 LUT 必要となる。ZYBO Z7-20 やPYNQ に実装しようとすると総LUT数は53,200 個なので全く足りない。よって 1 クロックで 1 出力の実装は無理だということが分かった。

目標を変更することにしようと思う。
1クロックで1判定出力でなく、画像がストリームで 1 クロックで 1 ピクセル送られてくるときに画像総ピクセル数のクロック+レイテンシのクロック数で変換するような畳み込みニューラルネットワークを Vivado HLS で作ろうと思う。これならば、畳み込み演算部は今までやってきたラプラシアンフィルタの回路を多少修正して適用すれば良い。つまり、白線間走行用畳み込みニューラルネットワークの場合は畳み込みのフィルタ数は 2 個なので、 853 x 2 = 1,706 LUT、程度演算部では使用すれば良いことになる。
後は、全結合層の演算がうまく画像総ピクセル数のクロックで行けるように C ソースコードを書いてみようと思う。

白線間走行用畳み込みニューラルネットワークだと 10 x 56 = 560 クロック+レイテンシで判定出力できるように作ってみよう。
  1. 2018年02月01日 21:28 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

白線間走行畳み込みニューラルネットワーク のアーキテクチャの図を出力した

@yu4u さんの”畳み込みニューラルネットワークをKeras風に定義するとアーキテクチャを図示してくれるツールを作った”を使わせて頂いて、自分の白線間走行畳み込みニューラルネットワーク のアーキテクチャの図を出力する。

まずは、Github の yu4u/convnet-drawer から Clone or download ボタンをクリックして、Download ZIP を選択して、convert-drawer-master.zip ファイルをダウンロードした。

convert-drawer-master.zip ファイルを展開すると、convert-drawer-master フォルダがあったので、それをWindows 10 のドキュメント・フォルダにコピーした。そして、convert-drawer-master フォルダの名前を convert-drawer フォルダに変更した。

まずは、convert-drawer フォルダに入って、AlexNet.svg を Inkscape で見てみた。
CNN_Figure_1_180104.png

凄い。それらしくアーキテクチャ図が表示される。

それじゃ、AlexNet.py をコピーして、curve_tracing_cnn.py に名前を変更し、白線間走行CNN のアーキテクチャに合うように書き換えた。
CNN_Figure_2_180104.png

Windows Powershell を起動して、python .\curve_tracing_cnn.py を実行した。
すると、curve_tracing_cnn.svg ファイルが生成された。
CNN_Figure_3_180104.png

curve_tracing_cnn.svg ファイルを Inkscape で開いた。
CNN_Figure_4_180104.png

かっこよい図ができた。これは良い。使わせて頂こう。だいぶシンプルな畳み込みニューラルネットワークで白線間走行している。

curve_tracing_cnn.py を貼っておく。

# curve_tracing_cnn.py
# 2018/01/04

import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from convnet_drawer import Model, Conv2D, MaxPooling2D, Flatten, Dense


def main():
    model = Model(input_shape=(10, 56, 1))
    model.add(Conv2D(2, (5, 5), (1, 1)))
    model.add(MaxPooling2D((2, 2), strides=(2, 2)))
    model.add(Flatten())
    model.add(Dense(100))
    model.add(Dense(3))
    model.save_fig(os.path.splitext(os.path.basename(__file__))[0] + ".svg")


if __name__ == '__main__':
    main()

  1. 2018年01月04日 12:40 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

カーブ、直線用白線間走行用畳み込みニューラルネットワーク18(ミニ・ロボットカーでの走行テスト)

カーブ、直線用白線間走行用畳み込みニューラルネットワーク17(BOOT.bin と devicetree.dtb の生成)”の続き。

前回は、SDK でBOOT.bin を作成し、zynq-zybo.dts から devicetree.dtb を作成して、Ubuntu にマウントした、MicroSDカードの第1パーティションにSFTPで書き込んだ。今回は、カーブと直線の白線間走行用のアプリケーションソフト(wl_tracing_cnn.cpp)を作成して、ミニ・ロボットカーで実際に白線間を走行させてみる。

ZYBO のZybot ディレクトリに wl_tracing_cnn_curve ディレクトリを作成して、Vivado HLS で作成したIP のドライバや、”白線追従走行用畳み込みニューラルネットワーク・システムの製作7(白線間を走行)”で作成した wl_tracing_cnn.cpp や Makefile をコピーした。

次に、wl_tracing_cnn.cpp を curve_conv_nn2_axi3 用に修正した。
curve_tracing_cnn_94_171228.png

Makefile もそれに合わせて修正した。
curve_tracing_cnn_95_171228.png

wl_tracing_cnn_curve ディレクトリを示す。
curve_tracing_cnn_96_171228.png

make コマンドを実行した。
curve_tracing_cnn_97_171228.png

make コマンド後の wl_tracing_cnn_curve ディレクトリを示す。
curve_tracing_cnn_98_171228.png

wl_tracing_cnn 実行ファイルができている。

./udmabuf_insmod を実行して、カメラのフレームバッファを確保してから、
./wl_tracing_cnn を実行する。
curve_tracing_cnn_99_171228.png

./wl_tracing_cnn の実行結果を示す。
curve_tracing_cnn_100_171228.png

実際にミニ・ロボットカーがカーブを走っているところを動画で示す。
まずは、48 度のカーブでのミニ・ロボットカーの走行動画を示す。






23度のカーブでのミニ・ロボットカーの走行動画を示す。




時々、左旋回、右旋回を迷うことがあるが、概ね安定して走行している。

wl_tracing_cnn.cpp を貼っておく。

// wl_tracing_cnn.cpp
// 2017/12/27 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

#include "xpwm.h"
#include "xdmar4resize_gray.h"
#include "xresize_gray.h"
#include "xcurve_conv_nn2_axis3.h"

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

#define HORIZONTAL_PIXEL    800
#define ALL_CHAR_OF_1LINE   (HORIZONTAL_PIXEL/8)
#define VERTICAL_PIXEL      600
#define ALL_CHAR_OF_ROW     (VERTICAL_PIXEL/8)
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXEL*VERTICAL_PIXEL*4)
#define ALL_DISP_CHARACTOR  (HORIZONTAL_PIXEL*VERTICAL_PIXEL)

#define DEBUG
//#define MOTOR_OFF

void cam_i2c_init(volatile unsigned *mt9d111_axi_iic) {
    mt9d111_axi_iic[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_axi_iic[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_axi_iic, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_axi_iic[66] = 0x100 | (device_addr & 0xfe); // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_axi_iic[66] = write_addr;
    mt9d111_axi_iic[66] = (write_data >> 8)|0xff;           // first data
    mt9d111_axi_iic[66] = 0x200 | (write_data & 0xff);      // second data
    cam_i2x_write_sync();
}

// Motor
//
void motor_settings(XPwm *motorLp, XPwm *motorRp){
    XPwm_DisableAutoRestart(motorLp);
    while(!XPwm_IsIdle(motorLp)) ;
    XPwm_Start(motorLp);
    XPwm_EnableAutoRestart(motorLp);
    
    XPwm_DisableAutoRestart(motorRp);
    while(!XPwm_IsIdle(motorRp)) ;
    XPwm_Start(motorRp);
    XPwm_EnableAutoRestart(motorRp);
}

void Stopped_Zybot(XPwm *motorLp, XPwm *motorRp){
    XPwm_Set_sw_late_V(motorLp, 0);
    XPwm_Set_sw_late_V(motorRp, 0);
}


int main()
{
    int fd0, fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd8, fd9, fd10;
    volatile unsigned *bmdc_axi_lites0, *bmdc_axi_lites1;
    volatile unsigned *dmaw4gabor_0;
    volatile unsigned *axis_switch_0, *axis_switch_1;
    volatile unsigned *mt9d111_inf_axis_0;
    volatile unsigned *mt9d111_axi_iic;
    volatile unsigned *axi_gpio_0;
    volatile unsigned *frame_buffer_bmdc;
    char  attr[1024];
    unsigned long  phys_addr;
    int i;
    XDmar4resize_gray xdmar;
    XResize_gray resg;
    XCurve_conv_nn2_axis3 stcnn;
    XPwm motorL, motorR;
    XPwm *motorLp, *motorRp;
    
    motorLp = &motorL;
    motorRp = &motorR;

    // Bitmap Display Controller 0 AXI4 Lite Slave (UIO6)
    fd6 = open("/dev/uio6", O_RDWR); // bitmap_display_controller 0 axi4 lite
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (bitmap_disp_cntrler_axi_master_0) open error\n");
        exit(-1);
    }
    bmdc_axi_lites0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!bmdc_axi_lites0){
        fprintf(stderr, "bmdc_axi_lites0 mmap error\n");
        exit(-1);
    }
    
    // Bitmap Display Controller 1 AXI4 Lite Slave (UIO7)
    fd7 = open("/dev/uio7", O_RDWR); // bitmap_display_controller axi4 lite
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (bitmap_disp_cntrler_axi_master_0) open error\n");
        exit(-1);
    }
    bmdc_axi_lites1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (!bmdc_axi_lites1){
        fprintf(stderr, "bmdc_axi_lites1 mmap error\n");
        exit(-1);
    }

    // dmaw4gabor_0 (UIO1)
    fd1 = open("/dev/uio1", O_RDWR); // dmaw4gabor_0 interface AXI4 Lite Slave
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (dmaw4gabor_0) open error\n");
        exit(-1);
    }
    dmaw4gabor_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (!dmaw4gabor_0){
        fprintf(stderr, "dmaw4gabor_0 mmap error\n");
        exit(-1);
    }
    
    // mt9d111 i2c AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // mt9d111 i2c AXI4 Lite Slave
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 (mt9d111_axi_iic) open error\n");
        exit(-1);
    }
    mt9d111_axi_iic = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!mt9d111_axi_iic){
        fprintf(stderr, "mt9d111_axi_iic mmap error\n");
        exit(-1);
    }

    // mt9d111 inf axis AXI4 Lite Slave (UIO5)
    fd5 = open("/dev/uio5", O_RDWR); // mt9d111 inf axis AXI4 Lite Slave
    if (fd5 < 1){
        fprintf(stderr, "/dev/uio5 (mt9d111_inf_axis_0) open error\n");
        exit(-1);
    }
    mt9d111_inf_axis_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd5, 0);
    if (!mt9d111_inf_axis_0){
        fprintf(stderr, "mt9d111_inf_axis_0 mmap error\n");
        exit(-1);
    }

    // axis_switch_0 (UIO2)
    fd2 = open("/dev/uio2", O_RDWR); // axis_switch_0 interface AXI4 Lite Slave
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (axis_switch_0) open error\n");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!axis_switch_0){
        fprintf(stderr, "axis_switch_0 mmap error\n");
        exit(-1);
    }
    
    // axis_switch_1 (UIO3)
    fd3 = open("/dev/uio3", O_RDWR); // axis_switch_1 interface AXI4 Lite Slave
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (axis_switch_1) open error\n");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!axis_switch_1){
        fprintf(stderr, "axis_switch_1 mmap error\n");
        exit(-1);
    }
    
    // axi_gpio_0 (UIO8)
    fd8 = open("/dev/uio8", O_RDWR); // axi_gpio_0 interface AXI4 Lite Slave
    if (fd8 < 1){
        fprintf(stderr, "/dev/uio8 (axi_gpio_0) open error\n");
        exit(-1);
    }
    axi_gpio_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd8, 0);
    if (!axi_gpio_0){
        fprintf(stderr, "axi_gpio_8 mmap error\n");
        exit(-1);
    }
    
    // udmabuf0
    fd9 = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd9 == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    frame_buffer_bmdc = (volatile unsigned *)mmap(NULL, 5760000, PROT_READ|PROT_WRITE, MAP_SHARED, fd9, 0);
    if (!frame_buffer_bmdc){
        fprintf(stderr, "frame_buffer_bmdc mmap error\n");
        exit(-1);
    }

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_1[16] = 0x0// 0x40 = 0
    axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000, disable
    axis_switch_1[18] = 0x80000000// 0x48 = 0x80000000, disable
    axis_switch_1[19] = 0x80000000// 0x4C = 0x80000000, disable
    axis_switch_1[0] = 0x2// Comit registers
    
    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_0[16] = 0x0// 0x40 = 0;
    axis_switch_0[0] = 0x2// Comit registers
    
    // phys_addr of udmabuf0
    fd10 = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fd10 == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open error\n");
        exit(-1);
    }
    read(fd10, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd10);
    printf("phys_addr = %x\n", (int)phys_addr);
    
    // DMAW4Gabor Initialization sequence
    dmaw4gabor_0[6] = (unsigned int)phys_addr; // Data signal of frame_buffer0
    dmaw4gabor_0[8] = (unsigned int)phys_addr+ALL_DISP_ADDRESS; // Data signal of frame_buffer1
    dmaw4gabor_0[0] = 0x1// ap_start = 1
    dmaw4gabor_0[0] = 0x80// auto_restart = 1

    // bitmap display controller settings
    bmdc_axi_lites0[0] = (unsigned int)phys_addr; // Bitmap Display Controller 0 start
    bmdc_axi_lites1[0] = (unsigned int)phys_addr; // Bitmap Display Controller 1 start
    mt9d111_inf_axis_0[0] = (unsigned int)phys_addr; // Camera Interface start (Address is dummy)

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(mt9d111_axi_iic);
    
    cam_i2c_write(mt9d111_axi_iic, 0xba, 0xf00x1);        // Changed regster map to IFP page 1
    cam_i2c_write(mt9d111_axi_iic, 0xba, 0x970x20);   // RGB Mode, RGB565

    mt9d111_inf_axis_0[1] = 0;

    // Initialization of xdmar4resize_gray, xresize_gray, XCurve_conv_nn2_axis3
    if(XDmar4resize_gray_Initialize(&xdmar, "dmar4resize_gray_0") != XST_SUCCESS){
        fprintf(stderr, "dmar4resize_gray_0 open error\n");
        exit(-1);
    }
    if(XResize_gray_Initialize(&resg, "resize_gray_0") != XST_SUCCESS){
        fprintf(stderr, "resize_gray_0 open error\n");
        exit(-1);
    }
    if(XCurve_conv_nn2_axis3_Initialize(&stcnn, "curve_conv_nn2_axis3_0") != XST_SUCCESS){
        fprintf(stderr, "curve_conv_nn2_axis3 open error\n");
        exit(-1);
    }

    XDmar4resize_gray_Set_frame_buffer0(&xdmar ,(unsigned int)phys_addr);
    XDmar4resize_gray_Set_frame_buffer1(&xdmar ,(unsigned int)phys_addr+ALL_DISP_ADDRESS);
    XDmar4resize_gray_Start(&xdmar);
    XDmar4resize_gray_EnableAutoRestart(&xdmar);

    XResize_gray_Start(&resg);
    XResize_gray_EnableAutoRestart(&resg);

    XCurve_conv_nn2_axis3_Start(&stcnn);
    XCurve_conv_nn2_axis3_EnableAutoRestart(&stcnn);

    // Initialization of motor
    if (XPwm_Initialize(motorLp, "pwm_0") != XST_SUCCESS){
        fprintf(stderr,"pwm_0 (Left) open error\n");
        exit(-1);
    }
    if (XPwm_Initialize(motorRp, "pwm_1") != XST_SUCCESS){
        fprintf(stderr,"pwm_1 (Right) open error\n");
        exit(-1);
    }
    // The Motors is rotated in the forward direction.
    XPwm_Set_sw_late_V(motorLp, 0);
    XPwm_Set_dir_V(motorLp, 1);

    XPwm_Set_sw_late_V(motorRp, 0);
    XPwm_Set_dir_V(motorRp, 0);

    motor_settings(motorLp, motorRp);

    // main loop
    printf("White line Tracking start. \n");
    while(1){
        usleep(10000); // 10 ms Wait

        while(!XCurve_conv_nn2_axis3_Get_outs_V_vld(&stcnn)) ;
        switch((int)XCurve_conv_nn2_axis3_Get_outs_V(&stcnn)){
            case 0 : // left turn
#ifndef MOTOR_OFF
                XPwm_Set_sw_late_V(&motorL, 10);
                XPwm_Set_sw_late_V(&motorR, 25);
#endif
#ifdef DEBUG
                printf("Left turn\n"); fflush(stdout);
#endif
                break;
            case 1 : // straight
#ifndef MOTOR_OFF
                XPwm_Set_sw_late_V(&motorL, 20);
                XPwm_Set_sw_late_V(&motorR, 20);
#endif
#ifdef DEBUG
                printf("Go straight\n"); fflush(stdout);
#endif
                break;
            default : // 2, right turn
#ifndef MOTOR_OFF
                XPwm_Set_sw_late_V(&motorL, 25);
                XPwm_Set_sw_late_V(&motorR, 10);
#endif
#ifdef DEBUG
                printf("Right turn\n"); fflush(stdout);
#endif
        }
    }


    munmap((void *)bmdc_axi_lites0, 0x10000);
    munmap((void *)bmdc_axi_lites1, 0x10000);
    munmap((void *)dmaw4gabor_0, 0x10000);
    munmap((void *)mt9d111_inf_axis_0, 0x10000);
    munmap((void *)mt9d111_axi_iic, 0x10000);
    munmap((void *)axis_switch_0, 0x10000);
    munmap((void *)axis_switch_1, 0x10000);
    munmap((void *)axi_gpio_0, 0x10000);
    munmap((void *)frame_buffer_bmdc, 576000);
    
    close(fd0);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd5);
    close(fd6);
    close(fd7);
    close(fd8);
    close(fd9);
    
    return(0);
}


Makefile を貼っておく。

# Makefile(wl_tracing_cnn)
# Referred to http://www.ie.u-ryukyu.ac.jp/~e085739/c.makefile.tuts.html

PROGRAM = wl_tracing_cnn
OBJS = wl_tracing_cnn.o xpwm_linux.o xpwm.o xdmar4resize_gray_linux.o xdmar4resize_gray.o xresize_gray_linux.o xresize_gray.o xcurve_conv_nn2_axis3_linux.o xcurve_conv_nn2_axis3.o

CC = gcc
CFLAGS = -Wall -O2

.SUFFIXES: .c .o

.PHONY: all

all: wl_tracing_cnn

wl_tracing_cnn: $(OBJS)
    $(CC) -Wall -o $@ $(OBJS)
    
.c.o:
    $(CC) $(CFLAGS) -c $<

    
.PHONY: clean
clean:
    $(RM) $(PROGRAM) $(OBJS)

  1. 2017年12月28日 05:51 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

カーブ、直線用白線間走行用畳み込みニューラルネットワーク17(BOOT.bin と devicetree.dtb の生成)

カーブ、直線用白線間走行用畳み込みニューラルネットワーク16(SDK)”の続き。

前回は、ハードウェアをエクスポートして、SDKを立ち上げ、アプリケーションソフトを作成して動作を確認した。今回は、SDK でBOOT.bin を作成し、zynq-zybo.dts から devicetree.dtb を作成してみよう。

まずは、BOOT.bin から作成する。
SDK で、File メニューからNew -> Application Project を選択する。
Application project ダイアログで、Project name にFSBL と入力して、Next > ボタンをクリックする。
curve_tracing_cnn_82_171227.png

Templates で Zynq FSBL を選択して、Finish ボタンをクリックする。
curve_tracing_cnn_83_171227.png

FSBL と FSBL_bsp フォルダができた。
curve_tracing_cnn_89_171227.png

FSBL フォルダを右クリックし、右クリックメニューからCreate Boot Image を選択する。
Create Boot Image ダイアログが表示された。
Boot image partitions のAdd ボタンをクリックして、u-boot.elf を追加した。
Create Image ボタンをクリックして、BOOT.bin を作成した。
curve_tracing_cnn_84_171227.png

BOOT.bin が作成された。
curve_tracing_cnn_85_171227.png

次に、devicetree.dtb を作成する。
今度は、Ubuntu 16.04 上で作業する。
白線追従走行用畳み込みニューラルネットワーク・システムの製作4(BOOT.binとデバイスツリー)”の zynq_zybo.dts で straight_conv_nn2_axis2 のエントリを編集して、curve_conv_nn2_axi3_0 を追加した。
curve_tracing_cnn_86_171227.png

zynq_zybo.dts を dtc でコンパイルする。
dtc -I dts -O dtb -o devicetree.dtb zynq-zybo.dts
コマンドでコンパイルして、devicetree.dtb を生成した。
curve_tracing_cnn_87_171227.png

curve_tracing_cnn_88_171227.png

今回は、ZYBO のMicroSDカードの第1パーティションをマウントして、WinSCP を使ってSFTP でファイルを書き込んでみよう。

まずはマウント・ポイントの ZYBO_BOOT ディレクトリを /media ディレクトリに作成する。
sudo mkdir /media/ZYBO_BOOT
次に、ZYBOのFAT32の第1パーティションをすべてのユーザー読み書き可でLinuxにマウントする。
sudo mount -t vfat /dev/mmcblk0p1 /media/ZYBO_BOOT -o rw,umask=000
ls /media/ZYBO_BOOT/ でZYBO_BOOT ディレクトリを見るとmount コマンドが成功している。
curve_tracing_cnn_90_171227.png

ZYBO の /media/ZYBO_BOOT/ に WinSCP で SFTP をして、先ほど作成した BOOT.bin と devicetree.dtb をアップロードした。
curve_tracing_cnn_91_171227.png

もう一度、ls /media/ZYBO_BOOT/ でZYBO_BOOT ディレクトリを見るとファイルが増えているのが分かる。元々あったBoot.bin と devicetree.dtb は、それぞれ _temp を名前に追加してリネームしてある。
curve_tracing_cnn_92_171227.png

これで、ZYBO のMicroSDカードの第1パーティション(ブート用FAT32 フォーマット)を書き換えることができたので、reboot すれば今コピーしたBoot.bin と devicetree.dtb が反映される。
sudo reboot コマンドでリブートを行った。

/sys/devices/amba.0 を見ると、43cf0000.curve_conv_nn2_axis3_0 があるのが分かる。
curve_tracing_cnn_93_171227.png

これで、ハードウェアの準備は終了した。次はアプリケーション・ソフトを作成して、ミニ・ロボットカーを走らせてみよう。

最後に zynq-zybo.dts を貼っておく。

/*
 * Device Tree for Zybo board
 * Partially generated by Device Tree Generator 1.1
 *
 * (C) Copyright 2007-2013 Xilinx, Inc.
 * (C) Copyright 2007-2013 Michal Simek
 * (C) Copyright 2007-2012 PetaLogix Qld Pty Ltd
 * (C) Copyright 2014 Digilent, Inc. 
 *
 * Michal SIMEK <monstr@monstr.eu>
 * Tinghui Wang <steven.wang@digilentinc.com> 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *
 */

/dts-v1/;
/ {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "xlnx,zynq-7000";
    model = "Xilinx Zynq";
    aliases {
        ethernet0 = &ps7_ethernet_0;
        serial0 = &ps7_uart_1;

        spi0 = &ps7_qspi_0;
    } ;
    chosen {
/*        bootargs = "console=ttyPS0,115200 root=/dev/ram rw earlyprintk"; */
        bootargs = "console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=1 coherent_pool=16M";
        linux,stdout-path = "/amba@0/serial@e0001000";
    } ;
    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        ps7_cortexa9_0: cpu@0 {
            bus-handle = <&ps7_axi_interconnect_0>;
            clock-latency = <1000>;
            clocks = <&clkc 3>;
            compatible = "arm,cortex-a9";
            device_type = "cpu";
            interrupt-handle = <&ps7_scugic_0>;
            /* operating-points = <666667 1000000 333334 1000000 >; */
            operating-points = <650000 1000000 >;
            reg = <0x0>;
        } ;
        ps7_cortexa9_1: cpu@1 {
            bus-handle = <&ps7_axi_interconnect_0>;
            clocks = <&clkc 3>;
            compatible = "arm,cortex-a9";
            device_type = "cpu";
            interrupt-handle = <&ps7_scugic_0>;
            reg = <0x1>;
        } ;
    } ;
    pmu {
        compatible = "arm,cortex-a9-pmu";
        interrupt-parent = <&ps7_scugic_0>;
        interrupts = <0 5 4>, <0 6 4>;
        reg = <0xf8891000 0x1000>, <0xf8893000 0x1000>;
        reg-names = "cpu0", "cpu1";
    } ;
    ps7_ddr_0: memory@0 {
        device_type = "memory";
        reg = <0x0 0x20000000>;
    } ;
    ps7_axi_interconnect_0: amba@0 {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "xlnx,ps7-axi-interconnect-1.00.a", "simple-bus";
        ranges ;
        ps7_afi_0: ps7-afi@f8008000 {
            compatible = "xlnx,ps7-afi-1.00.a";
            reg = <0xf8008000 0x1000>;
        } ;
        ps7_afi_1: ps7-afi@f8009000 {
            compatible = "xlnx,ps7-afi-1.00.a";
            reg = <0xf8009000 0x1000>;
        } ;
        ps7_afi_2: ps7-afi@f800a000 {
            compatible = "xlnx,ps7-afi-1.00.a";
            reg = <0xf800a000 0x1000>;
        } ;
        ps7_afi_3: ps7-afi@f800b000 {
            compatible = "xlnx,ps7-afi-1.00.a";
            reg = <0xf800b000 0x1000>;
        } ;
        ps7_ddrc_0: ps7-ddrc@f8006000 {
            compatible = "xlnx,zynq-ddrc-1.0";
            reg = <0xf8006000 0x1000>;
            xlnx,has-ecc = <0x0>;
        } ;
        ps7_dev_cfg_0: ps7-dev-cfg@f8007000 {
            clock-names = "ref_clk", "fclk0", "fclk1", "fclk2", "fclk3";
            clocks = <&clkc 12>, <&clkc 15>, <&clkc 16>, <&clkc 17>, <&clkc 18>;
            compatible = "xlnx,zynq-devcfg-1.0";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 8 4>;
            reg = <0xf8007000 0x100>;
        } ;
        ps7_dma_s: ps7-dma@f8003000 {
            #dma-cells = <1>;
            #dma-channels = <8>;
            #dma-requests = <4>;
            clock-names = "apb_pclk";
            clocks = <&clkc 27>;
            compatible = "arm,primecell", "arm,pl330";
            interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3",
                "dma4", "dma5", "dma6", "dma7";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 13 4>, <0 14 4>, <0 15 4>, <0 16 4>, <0 17 4>, <0 40 4>, <0 41 4>, <0 42 4>, <0 43 4>;
            reg = <0xf8003000 0x1000>;
        } ;
        ps7_ethernet_0: ps7-ethernet@e000b000 {
            #address-cells = <1>;
            #size-cells = <0>;
            clock-names = "ref_clk", "aper_clk";
            clocks = <&clkc 13>, <&clkc 30>;
            compatible = "xlnx,ps7-ethernet-1.00.a";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 22 4>;
            phy-handle = <&phy0>;
            phy-mode = "rgmii-id";
            reg = <0xe000b000 0x1000>;
            xlnx,eth-mode = <0x1>;
            xlnx,has-mdio = <0x1>;
            xlnx,ptp-enet-clock = <108333336>;
            mdio {
                #address-cells = <1>;
                #size-cells = <0>;
                phy0: phy@1 {
                    compatible = "realtek,RTL8211E";
                    device_type = "ethernet-phy";
                    reg = <1>;
                } ;
            } ;
        } ;
        ps7_globaltimer_0: ps7-globaltimer@f8f00200 {
            clocks = <&clkc 4>;
            compatible = "arm,cortex-a9-global-timer";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <1 11 0x301>;
            reg = <0xf8f00200 0x100>;
        } ;
        ps7_gpio_0: ps7-gpio@e000a000 {
            #gpio-cells = <2>;
            clocks = <&clkc 42>;
            compatible = "xlnx,zynq-gpio-1.0";
            emio-gpio-width = <64>;
            gpio-controller ;
            gpio-mask-high = <0xc0000>;
            gpio-mask-low = <0xfe81>;
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 20 4>;
            reg = <0xe000a000 0x1000>;
        } ;
        ps7_iop_bus_config_0: ps7-iop-bus-config@e0200000 {
            compatible = "xlnx,ps7-iop-bus-config-1.00.a";
            reg = <0xe0200000 0x1000>;
        } ;
        ps7_ocmc_0: ps7-ocmc@f800c000 {
            compatible = "xlnx,zynq-ocmc-1.0";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 3 4>;
            reg = <0xf800c000 0x1000>;
        } ;
        ps7_pl310_0: ps7-pl310@f8f02000 {
            arm,data-latency = <3 2 2>;
            arm,tag-latency = <2 2 2>;
            cache-level = <2>;
            cache-unified ;
            compatible = "arm,pl310-cache";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 2 4>;
            reg = <0xf8f02000 0x1000>;
        } ;
        ps7_qspi_0: ps7-qspi@e000d000 {
            clock-names = "ref_clk", "pclk";
            clocks = <&clkc 10>, <&clkc 43>;
            compatible = "xlnx,zynq-qspi-1.0";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 19 4>;
            is-dual = <0>;
            num-cs = <1>;
            reg = <0xe000d000 0x1000>;
            xlnx,fb-clk = <0x1>;
            xlnx,qspi-mode = <0x0>;
            #address-cells = <1>;
            #size-cells = <0>;
            flash@0 {
                compatible = "n25q128";
                reg = <0x0>;
                spi-tx-bus-width = <1>;
                spi-rx-bus-width = <4>;
                spi-max-frequency = <50000000>;
                #address-cells = <1>;
                #size-cells = <1>;
                partition@qspi-fsbl-uboot {
                    label = "qspi-fsbl-uboot";
                    reg = <0x0 0x400000>;
                };
                partition@qspi-linux {
                    label = "qspi-linux";
                    reg = <0x400000 0x500000>;
                };
                partition@qspi-device-tree {
                    label = "qspi-device-tree";
                    reg = <0x900000 0x20000>;
                };
                partition@qspi-user {
                    label = "qspi-user";
                    reg = <0x920000 0x6E0000>;
                };
            };

        } ;
        ps7_qspi_linear_0: ps7-qspi-linear@fc000000 {
            clock-names = "ref_clk", "aper_clk";
            clocks = <&clkc 10>, <&clkc 43>;
            compatible = "xlnx,ps7-qspi-linear-1.00.a";
            reg = <0xfc000000 0x1000000>;
        } ;
        ps7_scugic_0: ps7-scugic@f8f01000 {
            #address-cells = <2>;
            #interrupt-cells = <3>;
            #size-cells = <1>;
            compatible = "arm,cortex-a9-gic", "arm,gic";
            interrupt-controller ;
            num_cpus = <2>;
            num_interrupts = <96>;
            reg = <0xf8f01000 0x1000>, <0xf8f00100 0x100>;
        } ;
        ps7_scutimer_0: ps7-scutimer@f8f00600 {
            clocks = <&clkc 4>;
            compatible = "arm,cortex-a9-twd-timer";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <1 13 0x301>;
            reg = <0xf8f00600 0x20>;
        } ;
        ps7_scuwdt_0: ps7-scuwdt@f8f00620 {
            clocks = <&clkc 4>;
            compatible = "xlnx,ps7-scuwdt-1.00.a";
            device_type = "watchdog";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <1 14 0x301>;
            reg = <0xf8f00620 0xe0>;
        } ;
        ps7_sd_0: ps7-sdio@e0100000 {
            clock-frequency = <50000000>;
            clock-names = "clk_xin", "clk_ahb";
            clocks = <&clkc 21>, <&clkc 32>;
            compatible = "arasan,sdhci-8.9a";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 24 4>;
            reg = <0xe0100000 0x1000>;
            xlnx,has-cd = <0x1>;
            xlnx,has-power = <0x0>;
            xlnx,has-wp = <0x1>;
        } ;
        ps7_slcr_0: ps7-slcr@f8000000 {
            #address-cells = <1>;
            #size-cells = <1>;
            compatible = "xlnx,zynq-slcr", "syscon";
            ranges ;
            reg = <0xf8000000 0x1000>;
            clkc: clkc@100 {
                #clock-cells = <1>;
                clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", "cpu_3or2x",
                    "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", "dci",
                    "lqspi", "smc", "pcap", "gem0", "gem1",
                    "fclk0", "fclk1", "fclk2", "fclk3", "can0",
                    "can1", "sdio0", "sdio1", "uart0", "uart1",
                    "spi0", "spi1", "dma", "usb0_aper", "usb1_aper",
                    "gem0_aper", "gem1_aper", "sdio0_aper", "sdio1_aper", "spi0_aper",
                    "spi1_aper", "can0_aper", "can1_aper", "i2c0_aper", "i2c1_aper",
                    "uart0_aper", "uart1_aper", "gpio_aper", "lqspi_aper", "smc_aper",
                    "swdt", "dbg_trc", "dbg_apb";
                compatible = "xlnx,ps7-clkc";
                fclk-enable = <0xf>;
                ps-clk-frequency = <50000000>;
                reg = <0x100 0x100>;
            } ;
        } ;
        ps7_ttc_0: ps7-ttc@f8001000 {
            clocks = <&clkc 6>;
            compatible = "cdns,ttc";
            interrupt-names = "ttc0", "ttc1", "ttc2";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 10 4>, <0 11 4>, <0 12 4>;
            reg = <0xf8001000 0x1000>;
        } ;
        ps7_uart_1: serial@e0001000 {
            clock-names = "uart_clk", "pclk";
            clocks = <&clkc 24>, <&clkc 41>;
            compatible = "xlnx,xuartps", "cdns,uart-r1p8";
            current-speed = <115200>;
            device_type = "serial";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 50 4>;
            port-number = <0>;
            reg = <0xe0001000 0x1000>;
            xlnx,has-modem = <0x0>;
        } ;
        ps7_usb_0: ps7-usb@e0002000 {
            clocks = <&clkc 28>;
            compatible = "xlnx,ps7-usb-1.00.a", "xlnx,zynq-usb-1.00.a";
            dr_mode = "host";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 21 4>;
            phy_type = "ulpi";
            reg = <0xe0002000 0x1000>;
            xlnx,usb-reset = "MIO 46";
        } ;
        ps7_xadc: ps7-xadc@f8007100 {
            clocks = <&clkc 12>;
            compatible = "xlnx,zynq-xadc-1.00.a";
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 7 4>;
            reg = <0xf8007100 0x20>;
        } ;
        
        mt9d111_axi_iic@0x41600000 {
            compatible = "generic-uio";
            reg = < 0x41600000 0x10000>;
        };
        dmaw4gabor_0@43cb0000 {
            compatible = "generic-uio";
            reg = < 0x43cb0000 0x10000 >;
        };
        axis_switch_0@0x43c10000 {
            compatible = "generic-uio";
            reg = < 0x43c10000 0x10000 >;
        };
        axis_switch_1@0x43c20000 {
            compatible = "generic-uio";
            reg = < 0x43c20000 0x10000 >;
        };
        lap_filter_axis_0@0x43c30000 {
            compatible = "generic-uio";
            reg = < 0x43c30000 0x10000>;
        };    
        mt9d111_inf_axis_0@0x43C40000 {
            compatible = "generic-uio";
            reg = < 0x43C40000 0x10000>;
        };
        bitmap_disp_cntrler_axi_master_0@0x43c00000 {
            compatible = "generic-uio";
            reg = < 0x43c00000 0x10000>;
        };
        bitmap_disp_cntrler_axi_master_1@0x43c50000 {
            compatible = "generic-uio";
            reg = < 0x43c50000 0x10000>;
        };
        axi_gpio_0@0x41200000 {
            compatible = "generic-uio";
            reg = < 0x41200000 0x10000>;
        };
        frame_buffer_bmdc@0x17800000 {
            compatible = "generic-uio";
            reg = < 0x17800000 0x1000000>;
        };
        pwm_0@0x43c60000 {
            compatible = "generic-uio";
            reg = < 0x43c60000 0x10000>;
        };
        pwm_1@0x43c70000 {
            compatible = "generic-uio";
            reg = < 0x43c70000 0x10000>;
        };
        motor_monitor_0@0x43c80000 {
            compatible = "generic-uio";
            reg = < 0x43c80000 0x10000>;
        };
        motor_monitor_1@0x43c90000 {
            compatible = "generic-uio";
            reg = < 0x43c90000 0x10000>;
        };
        dmar4resize_gray_0@0x43ca0000 {
            compatible = "generic-uio";
            reg = < 0x43ca0000 0x10000>;
        };
        rgb2hsv_0@0x43cc0000 {
            compatible = "generic-uio";
            reg = < 0x43cc0000 0x10000>;
        };
        ultrasoninc_sensor_inf_0@0x43cd0000 {
            compatible = "generic-uio";
            reg = < 0x43cd0000 0x10000>;
        };
        resize_gray_0@0x43ce0000 {
            compatible = "generic-uio";
            reg = < 0x43ce0000 0x10000>;
        };
        curve_conv_nn2_axis3_0@0x43cf0000 {
            compatible = "generic-uio";
            reg = < 0x43cf0000 0x10000>;
        };
    } ;
} ;

  1. 2017年12月27日 05:37 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

カーブ、直線用白線間走行用畳み込みニューラルネットワーク16(SDK)

カーブ、直線用白線間走行用畳み込みニューラルネットワーク15(Vivadoプロジェクト)”の続き。

前回は、 curve_conv_nn2_axis3 IP を使用し、直線用のVivado 2017.3 のプロジェクトを修正して、カーブと直線対応のミニ・ロボットカーの走行システムをVivado 2017.3 で作成した。今回は、ハードウェアをエクスポートして、SDKを立ち上げ、アプリケーションソフトを作成して動作を確認してみよう。

まずは、Vivado で File メニューからExport → Export Hardware... を選択して、ハードウェアをエクスポートした。

File メニューから Launch SDK を選択して、SDK を立ち上げた。

SDK で wl_tracing_cnn アプリケーション・プロジェクトを作成した。

wl_tracing_cnn.c を修正して、 wl_tracing_cnn アプリケーション・プロジェクトにコピーした。
curve_tracing_cnn_80_171226.png

ZYBO の電源をONした。コンフィギュレーション・モードはJTAG だ。

SDK の Xilinx メニューから Program FPG を選択して、FPGA にビット・ファイルをコンフィギュレーションした。

wl_tracing_cnn.elf を起動した。
VGA ポートに接続したディスプレイにカメラ画像が表示された。
シリアル・ポートに接続されたTera Term の画面に結果と 3 出力の値が表示された。
curve_tracing_cnn_81_171226.png

成功だ。

wl_tracing_cnn.c を貼っておく。

/* * wl_tracing_cnn.c * *  Created on: 2017/09/18 *      Author: Masaaki */
// ベアメタル・アプリケーションでは、CNN がきちんと動作しているを確認するのみとする
// なお、このアプリケーションはcam_disp3_axis.c を起動してカメラを表示状態にした後で起動する
// 2017/09/19 : 直進、左旋回、右旋回の各出力の値を表示するように変更
// 2017/12/12 : カーブ対応に更新
//

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "xil_io.h"
#include "xparameters.h"
#include "sleep.h"

#include "xdmaw4gabor.h"
#include "xdmar4resize_gray.h"
#include "xresize_gray.h"
#include "xcurve_conv_nn2_axis3.h"

#define NUMBER_OF_WRITE_FRAMES    3 // Note: If not at least 3 or more, the image is not displayed in succession.

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

#define FRAME_BUFFER_ADDRESS 0x10000000
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

void cam_i2c_init(volatile unsigned *mt9d111_i2c_axi_lites) {
    mt9d111_i2c_axi_lites[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_i2c_axi_lites[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_i2c_axi_lites, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_i2c_axi_lites[66] = 0x100 | (device_addr & 0xfe);   // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_i2c_axi_lites[66] = write_addr;
    mt9d111_i2c_axi_lites[66] = (write_data >> 8)|0xff;         // first data
    mt9d111_i2c_axi_lites[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main(){
    XDmar4resize_gray xdmar;
    XResize_gray resg;
    XCurve_conv_nn2_axis3 stcnn;
    XDmaw4gabor xdma4g;
    int i;
    uint32_t res;
    int result[4];

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x48), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    XDmaw4gabor_Initialize(&xdma4g, 0);
    XDmaw4gabor_Set_frame_buffer0(&xdma4g, FRAME_BUFFER_ADDRESS);
    XDmaw4gabor_Set_frame_buffer1(&xdma4g, FRAME_BUFFER_ADDRESS+ALL_DISP_ADDRESS);
    XDmaw4gabor_Start(&xdma4g);
    XDmaw4gabor_EnableAutoRestart(&xdma4g);

    // mt9d111_inf_axis_0, axi_iic_0, bitmap_disp_cntrler_axi_master_0
    volatile unsigned int *bmdc0_axi_lites;
    volatile unsigned int *bmdc1_axi_lites;
    volatile unsigned int *mt9d111_axi_lites;
    volatile unsigned int *mt9d111_i2c_axi_lites;

    bmdc0_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    bmdc1_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_1_BASEADDR;
    mt9d111_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_MT9D111_INF_AXIS_0_BASEADDR;
    mt9d111_i2c_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_AXI_IIC_0_BASEADDR;

    bmdc0_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Bitmap Display Controller 0 start
    bmdc1_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Bitmap Display Controller 1 start
    mt9d111_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Camera Interface start (Address is dummy)

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(mt9d111_i2c_axi_lites);

    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0xf00x1);      // Changed regster map to IFP page 1
    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0x970x20);        // RGB Mode, RGB565

    mt9d111_axi_lites[1] = 0// One_shot_mode is disabled

    XDmar4resize_gray_Initialize(&xdmar, 0);
    XResize_gray_Initialize(&resg, 0);
    XCurve_conv_nn2_axis3_Initialize(&stcnn, 0);

    XDmar4resize_gray_Set_frame_buffer0(&xdmar ,FRAME_BUFFER_ADDRESS);
    XDmar4resize_gray_Set_frame_buffer1(&xdmar ,FRAME_BUFFER_ADDRESS+ALL_DISP_ADDRESS);
    XDmar4resize_gray_Start(&xdmar);
    XDmar4resize_gray_EnableAutoRestart(&xdmar);

    XResize_gray_Start(&resg);
    XResize_gray_EnableAutoRestart(&resg);

    XCurve_conv_nn2_axis3_Start(&stcnn);
    XCurve_conv_nn2_axis3_EnableAutoRestart(&stcnn);

    while(1){
        sleep(1);

        while(!XCurve_conv_nn2_axis3_Get_outs_V_vld(&stcnn)) ;
        printf("out = %d\n", (int)XCurve_conv_nn2_axis3_Get_outs_V(&stcnn));
        for(i=0; i<2; i++){
            XCurve_conv_nn2_axis3_Read_dot2_V_Words(&stcnn, i, &res, 1);
            result[i*2] = res & 0xffff;
            if(result[i*2] & 0x8000// minus
                result[i*2] = 0xffff0000 | result[i*2]; // Sign extension

            result[i*2+1] = (res & 0xffff0000) >> 16;
            if(result[i*2+1] & 0x8000// minus
                result[i*2+1] = 0xffff0000 | result[i*2+1]; // Sign extension
        }
        for(i=0; i<3; i++)
            printf("result[%d] = %x ", i , result[i]);
        printf("\n");
    }

    return(0);
}


  1. 2017年12月26日 05:14 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0