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

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

FPGAの部屋

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

ZYBO Z7-20 の u-boot.elf と devicetree.dtb を作る

白線間追従走行用畳み込みニューラルネットワークを走らせるためのMicro SD カードをZYBO Z7-20 対応にすることを目的としてやってみよう。
今回は、ZYBO Z7-20 用の u-boot.elf と devicetree.dtb を作ってみようと思う。

石原ひでみさんによると ZYBO Z7-20 用の u-boot は ZedBoard 用で代用できるということだったので、それでやってみよう。

バージョンが古いが「白線間追従走行用畳み込みニューラルネットワークを走らせるためのMicro SD カード」でも使用しているDigilentのGithubからダウンロードした u-boot をダウンロードして、ビルドしてみよう。
なお、「Vivado and zybo linux勉強会資料3」の 172 ページからを参照のこと。

まずは、VirtualBox 上のUbuntu 16.04 のホームディレクトリに ZYBO_Linux ディレクトリを作成した。
mkdir ZYBO_Linux

ZYBO_Linux ディレクトリに入った。
cd ZYBO_Linux

Digilent 社の u-boot のリポジトリをクローンした。
git clone -b master-next https://github.com/DigilentInc/u-boot-Digilent-Dev.git

u-boot-Digilent-Dev ディレクトリが生成された。
ZYBO_Z7_31_170928.png

u-boot-Digilent-Dev ディレクトリに入った。
cd u-boot-Digilent-Dev

ZedBoard 用の設定値を反映するため、コンフィギュレーションを行った。
make CROSS_COMPILE=arm-xilinx-linux-gnueabi- zynq_zed_config

u-boot のビルドを行った。
make CROSS_COMPILE=arm-xilinx-linux-gnueabi-
ZYBO_Z7_32_170928.png

ビルドが終了した。
ZYBO_Z7_33_170928.png

u-boot が生成されている。
ZYBO_Z7_34_170928.png

u-boot をコピーして u-boot.elf を作った。
cp u-boot u-boot.elf
ZYBO_Z7_35_170928.png

次は、デバイスツリーを修正する。
まずは、drivers_ZYBO ディレクトリの下に drivers_ZYBO_Z7_0_172_8 ディレクトリを作成した。
白線追従走行用畳み込みニューラルネットワーク・システムの製作4(BOOT.binとデバイスツリー)”のzynq_zybo.dts を zynq_zybo_z7.dts として、中身を修正した。
まずは、クロックが 650 MHz から 666.667 MHz になっているので、修正した。それから、DDR3 SDRAM が 512 MB から 1 GB になっているので修正を行った。
ZYBO_Z7_36_170929.png

dtc コンパイラで zynq_zybo_z7.dts をコンパイルした。
dtc -I dts -O dtb -o devicetree.dtb zynq_zybo_z7.dts
ZYBO_Z7_37_170929.png

devicetree.dtb が生成された。

zynq_zybo_z7.dts を貼っておく。

/*
 * Device Tree for Zybo Z7 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
 *
 */
 /* for ZYBO Z7 by marsee 2017/09/29 */

/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 0x40000000>;
    } ;
    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>;
        };
        straight_conv_nn2_axis2_0@0x43cf0000 {
            compatible = "generic-uio";
            reg = < 0x43cf0000 0x10000>;
        };
    } ;
} ;

  1. 2017年09月29日 05:22 |
  2. ZYBO Z7
  3. | トラックバック:0
  4. | コメント:0

ZYBO Z7-20 のPS 設定とZYBO のPS 設定を比較する

ZYBO Z7-20 をLinux で使用するためにデバイスツリーが必要だが、その前にZYBO の u-boot が走るか?という問題がある。
そこで、ZYBO と ZYBO Z7-20 のPS の設定を違いを確かめてみることにした。

まずは、”ZYBO Z7-20 のボードファイルをVivado 2017.2 にインストールする”を見てZYBO Z7-20 用のボードファイルをVivado 2017.2 にインストールする必要がある。これをやらないとTCL スクリプトが途中で止まってしまう。

次に、”Zybo Z7 DMA Audio Demo”のページに行った。
ZYBO_Z7_13_170927.png

、”Zybo Z7 DMA Audio Demo”のページの”Zybo Z7-20 DMA Audio Project Repository”の”ZIP Archive”をクリックする。
ZYBO_Z7_14_170927.png

すると、Zybo-Z7-20-DMA-2016.4-2.zip がダウンロードできた。
ZYBO_Z7_15_170927.png

ZIP を解凍して、Zybo-Z7-20-DMA フォルダができた。
ZYBO_Z7_16_170927.png

Zybo-Z7-20-DMA フォルダの下の proj フォルダの下の create_project.tcl を起動すればVivado のプロジェクトが生成されるはず。
ZYBO_Z7_17_170927.png

Vivado 2017.2 を立ち上げ、Zybo-Z7-20-DMA フォルダの下の proj フォルダに行くために cd コマンドを実行した。
cd z:/ZYBO_Z7/Zybo-Z7-20-DMA/proj/
ZYBO_Z7_18_170927.png

次に、create_project.tcl を起動した。
source create_project.tcl
ZYBO_Z7_19_170927.png

ブロックデザインが構築された。
IP をアップグレードする必要あるようだ。Report IP Status をクリックする。
ZYBO_Z7_20_170927.png

Upgrade Selected ボタンをクリックする。するとIP がアップグレードされる。
ZYBO_Z7_22_170927.png

Upgrade IP ダイアログでOK ボタンをクリックする。
ZYBO_Z7_23_170927.png

Generate Output Products ダイアログが表示される。Generate ボタンをクリックする。
ZYBO_Z7_24_170927.png

Critical Messages を示す。DDR の設定値がおかしい?ボードファイルがおかしいのだろうか?
ZYBO_Z7_25_170927.png

ジェネレートが終了したら、IP Status ウインドウで Rerun ボタンをクリックする。
ZYBO_Z7_26_170927.png

さて、ブロックデザインで、ZYNQのIP をダブルクリックして、設定を開く。
ZYBO_Z7_27_170927.png

ZYBO が左の画面で、ZYBO Z7-20 が左の画面だ。
ZYBO_Z7_28_170927.png

ZYBO はI2C があって、ZYBO Z7-20 では I2C は無く、GPIOがあった。

Peripheral I/O Pins をクリックして見てみよう。
ZYBO
ZYBO_Z7_29_170927.png

ZYBO Z7-20
ZYBO_Z7_30_170927.png

ZYBO のI2C はEMIOだったので、関係ない。ZYBO Z7-20 では、使用していないMIO はGPIOにマップされている。
これだと、ZYBO とZYBO Z7-20 のMIO ピンはほとんど同じだと言える。ZYBO Z7-20 の方がデバイスをマップした後のMIO ピンがすべてGPIO にマップされている。
  1. 2017年09月27日 20:38 |
  2. ZYBO Z7
  3. | トラックバック:0
  4. | コメント:0

ZYBO Z7-20 のボードファイルをVivado 2017.2 にインストールする

ZYBO Z7-20 をVivado で使いやすくするためにDigilent 社のボードファイルをVivado 2017.2 にインストールしよう。

ZYBO Z7 のページの”Installing Vivado Board Files for Digilent Boards”をクリックする。
ZYBO_Z7_5_170927.png

”Installing Vivado Board Files for Digilent Boards”ページに行った。
ZYBO_Z7_7_170927.png

Installing the board files for Vivado 2015.1”をクリックした。

”Vivado Version 2015.1 and Later Board File Installation”ページに飛んだ。
ZYBO_Z7_8_170927.png

Download”をクリックして、vivado-boards-master.zip をダウンロードした。
ZYBO_Z7_9_170927.png

ZIPファイルを展開してみると、ZYBO Z7-20 用のボードファイルも入っている。
ZYBO_Z7_10_170927.png

F:\Xilinx\Vivado\2017.2\data\boards\board_files にZYBO Z7-20 などのDigilent 社のボードファイルをコピー&ペーストした。
ZYBO_Z7_11_170927.png

Vivado 2017.2 を起動して、New Projct ウィザードの Default Part で、Select の Boards ボタンをクリックすると、下のリストにZYBO Z7-20 が表示されているのが分かる。
ZYBO_Z7_12_170927.png
  1. 2017年09月27日 04:48 |
  2. ZYBO Z7
  3. | トラックバック:0
  4. | コメント:0

ZYBO Z7-20 が届きました

ZYBO Z7-20 が昨日届きました。

注文したのは、2017年9月20日の夜です。25日に届いたので、5日で届きました。

価格は、ZYBO Z7-20 本体が &299、SDSoCバウチャーが $5、Shipping が $48.19 で合計 $352.19 USD でした。

Fedex で届きました。
ZYBO_Z7_1_170926.jpg

ZYBO Z7の箱とSDSoCバウチャーです。ナンバーは見せません。。。w
ZYBO_Z7_2_170926.jpg

ZYBO Z7-20 の表側です。
ZYBO_Z7_3_170926.jpg

HDMI が2つとMIPI 用のコネクタが付いています。

裏側。
ZYBO_Z7_4_170926.jpg

裏も実装密度高いですね。
  1. 2017年09月26日 05:04 |
  2. ZYBO Z7
  3. | トラックバック:0
  4. | コメント:0

「RTLを語る会(14) ~FPGAの現実~」で発表してきました

RTLを語る会(14) ~FPGAの現実~」で「「ゼロから作るDeep Learning」の畳み込みニューラルネットワークのハードウェア化」という題で1時間発表してきました。

SlideShare でも「「ゼロから作るDeep learning」の畳み込みニューラルネットワークのハードウェア化」のスライドを公開しました。
「ゼロから作るDeep learning」という本を読んで、MNIST の手書き数字の認識用の畳み込みニューラルネットワークをVivado HLSを使ってFPGA に実装したよ。。。というお話です。そういえばMNIST の手書き数字とはどこにも書いてなかったかも?修正します。
修正しました。MNIST の手書き数字認識用畳み込みニューラルネットワークと書いておきました。

いろいろとオフレコの話が聞けて面白かったです。オフレコなので、ブログに書けませんけどね。。。
  1. 2017年09月25日 04:32 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

Xilinx reVISION、xfOpenCV のExampleをやってみた1(of2)

Xilinx 社から機械学習やコンピュータ・ビジョン・ツールの reVision と、OpenCVをハードウェア化する xfOpenCV が出た。これらのExample をやってみることにした。

まずは、”reVISION Getting Started Guide 2017.2”を参考にして、やってみよう。
使用するOSはUbuntu 16.04 で、SDx は 2017.2 を使用した。

このチュートリアルを実行するためには、ZCU102 Evaluation Board が必要なのだが、持っていないので、reVision ツール(SDSoC)でコンパイルするところだけでもやってみよう。reVision は今のバージョンでは、そういう名前のツールがある訳ではなく、SDSoC を
ZCU102 用の reVision のプラットホームで動作させているようだ。

SDx 2017.2 のSDSoC をインストールした。次に、ZCU102 Production Silicon reVISION Package をダウンロードした。
revision_xfopencv_1_170922.png

zcu102_rv_ss をホームディレクトリに展開した。
revision_xfopencv_2_170922.png

SDx を起動するために、それ用の settings64.sh を起動して、、”reVISION Getting Started Guide 2017.2”に従って、SYSROOT 環境変数を指定した。
export SYSROOT=~/zcu102_rv_ss/sw/sysroot/
sdx をタイプして SDx (SDSoC) を起動した。
revision_xfopencv_3_170923.png

SDx の起動メッセージを示す。
revision_xfopencv_4_170923.png

SDx が起動した。Worksapce を指定した。
revision_xfopencv_5_170923.png

SDx の画面が表示された。
revision_xfopencv_6_170923.png

File メニューから New -> Xilinx SDx Project... を選択する。
New Project ダイアログが表示された。
Project name に of2 と入力して、Next > ボタンをクリックした。
revision_xfopencv_7_170923.png

Choose Hardware Platform で、Add Custom platform... ボタンをクリックした。
revision_xfopencv_8_170923.png

ホームディレクトリの下のzcu102_rv_ss ディレクトリを選択する。
revision_xfopencv_9_170923.png

すると、Choose Hardware Platform に zcu102_rv_ss[custom] が表示された。
zcu102_rv_ss[custom] を選択して、Next > ボタンをクリックした。
revision_xfopencv_10_170923.png

Choose Software Platform and Target CPU が表示された。デフォルトのまま、Next > ボタンをクリックした。
revision_xfopencv_11_170923.png

Template でLive IO のDense Optical Flow (2PPC) を選択し、Finish ボタンをクリックした。
revision_xfopencv_12_170923.png

of プロジェクトが表示された。
revision_xfopencv_13_170923.png

右上の方のActive build configuration をRelese に変更した。
revision_xfopencv_14_170923.png

左上のProject Explor の of2 プロジェクトを右クリックし、右クリックメニューからBuild Project を選択して、ビルドを行った。
Build Project ダイアログが表示されて、ビルドが進んだ
revision_xfopencv_15_170923.png

ビルド終了。i5の4プロセッサで1時間23分くらいかかった。
revision_xfopencv_16_170923.png
  1. 2017年09月24日 10:19 |
  2. reVISION, xfOpenCV
  3. | トラックバック:0
  4. | コメント:0

白線追従走行用畳み込みニューラルネットワーク・システムの製作7(白線間を走行)

白線追従走行用畳み込みニューラルネットワーク・システムの製作6(実機で確認2)”の続き。

前回は、SDKで進む方向を表示するアプリケーションソフトを製作して、問題がないことを確かめた。今回は、ZYBO のUbuntu 14.04 上でアプリケーションソフトを作り、白線間を走行させる。

Zybot/wl_tracing_cnn ディレクトリを作成し、wl_tracing_cnn.cpp アプリケーションソフトを作成した。
wl_tracing_cnn ディレクトリには、dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 のドライバを入れて、画像フレーム用のバッファ領域を確保するためにikwzm さんの udmabuf もコンパイル済みの状態で入っている。後、コンパイルするためのMakefile も作成した。
wlt_cnn_208_170922.png

make でコンパイルして wl_tracing_cnn を生成した。

ミニ・ロボットカーを白線間にセットして、udmabuf_insmod を実行してから、 wl_tracing_cnn を起動した。
白線間は走行したのだが、左右に振れてしまって真っ直ぐ走らない感じだ。例えば、左旋回して戻るときに白線間の真ん中あたりに車体がまっすぐになるように右に舵を切る必要がある。それを考えていなかったので左右に振れてしまっているのだと思う。
例えば、右旋回して戻るときに直進が無くて、すぐに左旋回になっているイメージだろうか?
動画を貼っておく。




現在の wl_tracing_cnn.cpp を貼っておく。

// wl_traceing_cnn.cpp
// 2017/09/18 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 "xstraight_conv_nn2_axis2.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;
    XStraight_conv_nn2_axis2 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, xstraight_conv_nn2_axis2
    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(XStraight_conv_nn2_axis2_Initialize(&stcnn, "straight_conv_nn2_axis2_0") != XST_SUCCESS){
        fprintf(stderr, "straight_conv_nn2_axis2 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);

    XStraight_conv_nn2_axis2_Start(&stcnn);
    XStraight_conv_nn2_axis2_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(!XStraight_conv_nn2_axis2_Get_outs_V_vld(&stcnn)) ;
        switch((int)XStraight_conv_nn2_axis2_Get_outs_V(&stcnn)){
            case 0 : // left turn
#ifndef MOTOR_OFF
                XPwm_Set_sw_late_V(&motorL, 15);
                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, 22);
                XPwm_Set_sw_late_V(&motorR, 22);
#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, 15);
#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_traceing_cnn)
# Referred to http://www.ie.u-ryukyu.ac.jp/~e085739/c.makefile.tuts.html

PROGRAM = wl_traceing_cnn
OBJS = wl_traceing_cnn.o xpwm_linux.o xpwm.o xdmar4resize_gray_linux.o xdmar4resize_gray.o xresize_gray_linux.o xresize_gray.o xstraight_conv_nn2_axis2_linux.o xstraight_conv_nn2_axis2.o

CC = gcc
CFLAGS = -Wall -O2

.SUFFIXES: .c .o

.PHONY: all

all: wl_traceing_cnn

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

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

  1. 2017年09月22日 04:19 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

白線追従走行用畳み込みニューラルネットワーク・システムの製作6(実機で確認2)

白線追従走行用畳み込みニューラルネットワーク・システムの製作5(実機で確認)”の続き。

前回は、白線追従走行用畳み込みニューラルネットワークが動作しているかどうか確認するために各出力の値を出力するように、Vivado HLS プロジェクトの straight_conv_nn2_axis3 を変更した。今回は、Vivado HLS プロジェクトの straight_conv_nn2_axis3 を変更したのだが、関数名は straight_conv_nn2_axis2 にしてあるので、今までのstraight_conv_nn2_axis2 IP と入れ替えることができるので、入れ替えて、ZYBO_0_172_8 フォルダのVivado プロジェクトでビットストリームの生成を行って、実機で試してみよう。

Vivado プロジェクトで論理合成、インプリメント、ビットストリームの生成を行った。結果を示す。
wlt_cnn_202_170919.png

問題ない。

ハードウェアをエクスポートして、SDK を立ち上げ、wl_tracing_cnn アプリケーションソフトを変更した。
wlt_cnn_203_170919.png

実機で確認すると、各出力の値も変化が無い。これはおかしい。
wl_tracing_cnn アプリケーションソフトを良く見直してみると、dmar4resize_gray のframe_buffer0 とframe_buffer1 にアドレスを入れていなかった。。。orz

dmar4resize_gray のframe_buffer0 とframe_buffer1 にアドレスをセットすると変化した。
wlt_cnn_204_170919.png

これで良さそう。

白線ボードに持っていってチェックした。
最初の状態では、出力は 0 で左旋回となった。
wlt_cnn_205_170921.jpg

2番目の状態では、出力は 1 で直進となった。
wlt_cnn_206_170921.jpg

3番目の状態では、出力は 2 で右旋回となった。
wlt_cnn_207_170921.jpg

大体良さそうだ。
後は、Linux 版の白線追従走行アプリケーションソフトを作って、ミニ・ロボットカーを走らせたい。

ベアメタル・アプリケーションの wl_tracing_cnn.c を貼っておく。

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

#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 "xstraight_conv_nn2_axis2.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;
    XStraight_conv_nn2_axis2 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);
    XStraight_conv_nn2_axis2_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);

    XStraight_conv_nn2_axis2_Start(&stcnn);
    XStraight_conv_nn2_axis2_EnableAutoRestart(&stcnn);

    while(1){
        sleep(1);

        while(!XStraight_conv_nn2_axis2_Get_outs_V_vld(&stcnn)) ;
        printf("out = %d\n", (int)XStraight_conv_nn2_axis2_Get_outs_V(&stcnn));
        for(i=0; i<2; i++){
            XStraight_conv_nn2_axis2_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年09月21日 05:14 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

白線追従走行用畳み込みニューラルネットワーク・システムの製作5(実機で確認)

白線追従走行用畳み込みニューラルネットワーク・システムの製作4(BOOT.binとデバイスツリー)”の続き。

前回は、BOOT.bin と devicetree.dtb を作成して、ZYBO でUbuntu を立ち上げ、デバイスが見えるかどうか?を確かめた。今回は、SDKを使用したベアメタル・アプリケーションで、実際に白線追従走行用畳み込みニューラルネットワークが動作するかどうか?を確かめる。

SDK で wl_tracing_cnn アプリケーション・プロジェクトを作成し、wl_tarcing_cnn.c を作成した。
wlt_cnn_196_170919.png

白線はないけど、何か結果が出ると思い、部屋の中の画像でやってみることにした。結果は、手とかをカメラの前で振っても結果は変わらずに 1 つまり直進だった。
wlt_cnn_197_170919.png

ちょっとこれでは、最大値の出力番号だけ表示しているため、値が変わっているのか?変わっていないのかが分からない?
そこで、straight_conv_nn2_axis2 のVivado HLS プロジェクトを少し変更することにした。これは、白線追従走行用畳み込みニューラルネットワークが入っているIP だ。
straight_conv_nn2_axis3 とプロジェクトを新規作成して、ファイル名もstraight_conv_nn2_axis3.cpp, straight_conv_nn2_axis3_tb.cpp と変更したが、関数の名前だけはstraight_conv_nn2_axis2 にしておいた。こうすると、IP にしたときの名前が同じなので、既存のstraight_conv_nn2_axis2 IP と取り換えることができる。
3つの出力を出力できるように変更した。
wlt_cnn_198_170919.png

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

Latency は前よりも 4 クロック増えている。
リソース使用量はBRAM_18K は増えているが、FF と LUT は減っている。
AXI4 Lite Slave のアドレスを示す。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of outs_V
//        bit 1~0 - outs_V[1:0] (Read)
//        others  - reserved
// 0x1c : Control signal of outs_V
//        bit 0  - outs_V_ap_vld (Read/COR)
//        others - reserved
// 0x20 ~
// 0x27 : Memory 'dot2_V' (3 * 16b)
//        Word n : bit [15: 0] - dot2_V[2n]
//                 bit [31:16] - dot2_V[2n+1]
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


dot2_V が増えているのが分かった。

次に、Export RTL を行った。こちらも結果を示す。
wlt_cnn_201_170919.png

こちらも問題ないようだ。これでIP となった。

straight_conv_nn2_axis3.cpp を貼っておく。

// straight_conv_nn2_axis3.cpp
// 2017/09/09 by marsee
// 畳み込み層のカーネル数 2
// AXI4 Stream入力 番号出力
// 2017/09/18 : dot2[3]の出力も追加
//

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

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#define REDUSED_ROW        45
#define REDUSED_COULMN    60
#define NUM_OF_KERNELS    2
#define COULMN_PIXELS    56
#define ROW_PIXELS        10
#define ALL_PIXELS        560
#define NUM_OF_OUTPUT    3

int max_ap_fixed(ap_fixed<167, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num);

int straight_conv_nn2_axis2(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs,
        ap_fixed<167, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT]){
#pragma HLS INTERFACE s_axilite port=dot2
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=outs
#pragma HLS INTERFACE axis register both port=ins
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<136, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<136, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<167, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_axiu<32,1,1,1> pix;

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

    // 10 x 56 に整形
    buf_copy1: for(int i=0; i<REDUSED_ROW; i++){
        buf_copy2: for(int j=0; j<REDUSED_COULMN; j++){
            if (!(i==0 && j==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            if((i>=33 && i<33+ROW_PIXELS) && (j>=2 && j<2+COULMN_PIXELS)){
                buf[i-33][j-2] = (ap_ufixed<80, AP_TRN_ZERO, AP_SAT>)((ap_ufixed<168, AP_TRN_ZERO, AP_SAT>)(pix.data & 0xff) / 256);
            }
        }
    }

    // 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++){
                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){
                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++){
                    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++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];
    }

    max_ap_fixed(dot2, outs);

    return(0);
}

int max_ap_fixed(ap_fixed<167, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num){
    int max_id;
    ap_fixed<167, AP_TRN_ZERO, AP_SAT> max;

    for(int i=0; i<NUM_OF_OUTPUT; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    out_num = (ap_uint<2>)max_id;

    return(0);
}


straight_conv_nn2_axis3_tb.cpp を貼っておく。

// straight_conv_nn2_axis3_tb.cpp
// 2017/09/09 by marsee
//
// 2017/09/18 : straight_conv_nn2_axis3.cpp に dot2[3]の出力も追加
//

#include <iostream>
#include "hls_opencv.h"
#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT    600
#define MAX_WIDTH    800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;

using namespace cv;

#define NUM_OF_OUTPUT    3

#define MAX_LOOP_COUNT    11
//#define MAX_LOOP_COUNT    1    // for C/RTL Co-Simulation

#define STRAIGHT_IMAGE_NAME        "straight"
#define LEFT_TURN_IMAGE_NAME    "left_turn"
#define RIGHT_TURN_IMAGE_NAME    "right_turn"

int straight_conv_nn2_axis2(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs,
        ap_fixed<167, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT]);
int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs);
int main_output_loop(char *buf);

int main () {
    char buf[200];

    sprintf(buf, "%s", STRAIGHT_IMAGE_NAME);
    main_output_loop(buf);

    sprintf(buf, "%s", LEFT_TURN_IMAGE_NAME);
    main_output_loop(buf);

    sprintf(buf, "%s", RIGHT_TURN_IMAGE_NAME);
    main_output_loop(buf);

    return(0);
}

int main_output_loop(char *buf){
    char bmp_file_name[200];
    ap_uint<2> outs;
    AXI_STREAM src_axi, dst_axi;
    Mat src;
    ap_fixed<167, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];

    for(int i=0; i<MAX_LOOP_COUNT; i++){
        sprintf(bmp_file_name, "%s%d.bmp", buf, i);

        // OpenCV で 画像を読み込む
        src = imread(bmp_file_name);

        // BGR から RGBへ変換
        Mat src_rgb;
        cvtColor(src, src_rgb, CV_BGR2RGB);

        // Mat フォーマットから AXI4 Stream へ変換
        cvMat2AXIvideo(src_rgb, src_axi);

        // resize_gray() 関数をコール
        resize_gray(src_axi, dst_axi);

        straight_conv_nn2_axis2(dst_axi, outs, dot2);

        printf("*%s\n", bmp_file_name);
        printf("outs = %d\n", (int)outs);
        for(int i=0; i<NUM_OF_OUTPUT; i++)
            printf("dot2[%d] = %f ", i, (float)dot2[i]);
        printf("\n");
    }

    return(0);
}

int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs){

    RGB_IMAGE org_img(600800);
    GRAY_IMAGE org_img_g(600800);
    GRAY_IMAGE resize_img_g(4560);
    RGB_IMAGE resize_img(4560);

    hls::AXIvideo2Mat(ins, org_img);
    hls::CvtColor<HLS_RGB2GRAY>(org_img, org_img_g);
    hls::Resize(org_img_g, resize_img_g);
    hls::CvtColor<HLS_GRAY2RGB>(resize_img_g, resize_img);
    hls::Mat2AXIvideo(resize_img, outs);

    return(0);
}


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

Sublime Textでカラースキーマを変更する

Sublime text 3 を使用していて、文字のハイライトなどのカラースキームを変更する方法を書いておく。

まずは、その名の通り”Sublime Textでカラースキーマを変更する”を参考にした。それを参考にすると、tmtheme-editor を使用すればいろいろなカラースキーマを選べて、色を変更することもできるようになる。
tmtheme-editor_1_170918.png

左のペインでカラースキーマを選んで、自分の感性に近いカラースキーマをそのままでもよいが、自分のより好みになるように変えて .tmTheme として、C:\Users\<ユーザー名>\AppData\Roaming\Sublime Text 3\Packages\User にセーブして、Sublime textを再起動すればそのカラースキーマを選択することができる。
tmtheme-editor_2_170918.png
  1. 2017年09月18日 11:57 |
  2. EDAツールについて
  3. | トラックバック:0
  4. | コメント:0

白線追従走行用畳み込みニューラルネットワーク・システムの製作4(BOOT.binとデバイスツリー)

白線追従走行用畳み込みニューラルネットワーク・システムの製作3(SDK)”の続き。

前回は、SDKでアプリケーションソフトを作成してカメラ画像が表示できることが確認できた。今回は、SDK でFSBL を作成してから、BOOT.bin を作成した。そして、デバイスツリーのソースをUbuntu 上でコンパイルして devicetree.dtb を作成し、ZYBO のMicro SD カードに書き込んで、/sys/devices/amba.0 以下のディレクトリを確認した。

最初に、SDK のFile メニューからNew -> Application Project を選択して、FSBL アプリケーション・プロジェクトを作成した。
wlt_cnn_186_170917.png

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

FSBL とFSBL_bsp が作成された。
wlt_cnn_188_170917.png

FSBL アプリケーション・プロジェクトで右クリックし、右クリックメニューから、Create Boot Image を選択する。
Create Boot Image ダイアログで、u-boot.elf を追加して、Create Image ボタンをクリックする。
wlt_cnn_189_170917.png

BOOT.bin が生成された。
wlt_cnn_190_170917.png

次に、Ubuntu でZYBO用のzynq-zybo.dts を編集して、dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 のエントリを追加した。
wlt_cnn_191_170917.png

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

ZYBO のMicro SD カードにBOOT.bin とdevicetree.dtb をコピー&ペーストした。
wlt_cnn_193_170917.png

Micro SD カードをZYBO に挿入して、電源ON でブートした。
/sys/devices/amba.0 を見ると、デバイスが表示された。
wlt_cnn_194_170917.png

dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 も入っていた。
uio の番号を表示した。やはり、zynq-zybo.dts に書いてある順番に番号が振られていく。
wlt_cnn_195_170917.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>;
        };
        straight_conv_nn2_axis2_0@0x43cf0000 {
            compatible = "generic-uio";
            reg = < 0x43cf0000 0x10000>;
        };
    } ;
} ;

  1. 2017年09月18日 07:28 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

白線追従走行用畳み込みニューラルネットワーク・システムの製作3(SDK)

”白線追従走行用畳み込みニューラルネットワーク・システムの製作2”の続き。

前回は白線追従走行用畳み込みニューラルネットワーク・システムのハードウェアが完成した。今回は、SDKを使用して、カメラ画像をディスプレイに表示するアプリケーションソフトを起動して、ハードウェアがとりあえず正しく動作するか?を確かめてみよう。

それでは、Vivado 2017.2 で、File メニューから Exprot -> Export Hardware... を選択し、Include bitstream のチェックを付けて、ハードウェアをエクスポートした。
File メニューからLaunch SDK を選択して、SDK を起動した。
SDK が起動した。
wlt_cnn_183_170916.png

前のハードウエア・プラットフォームとアプリケーション・プロジェクトなどがあるので、それらを削除して、新たに cam_disp3_axis アプリケーション・プロジェクトを作成した。
wlt_cnn_184_170916.png

ZYBO を接続し、SDK のXilinx Tools から Program FPGA を選択して、ビットストリームをダウンロードした。

cam_disp3_axis.elf を実行するとカメラの画像が表示された。
wlt_cnn_185_170917.jpg

カメラ画像が表示されたので、その部分のハードウェアには問題がないことが分かった。
これで、白線追従走行用アプリケーションソフトを作成すればテストができる。
  1. 2017年09月17日 04:14 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

白線追従走行用畳み込みニューラルネットワーク・システムの製作2

白線追従走行用畳み込みニューラルネットワーク・システムの製作1”の続き。

前回は、”dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 をVivado HLS 2017.2 で IP化”でIP が揃ったので、白線追従走行用畳み込みニューラルネットワーク・システムの製作を行ったが、straight_conv_nn2_axis2 でタイミングエラーが発生してしまった。今回は、straight_conv_nn2_axis2 のタイミング制約を厳しくして、Vivado HLS 2017.2 で再合成する。

Vivado HLS 2017.2 でターゲットを 7 ns にして、再度合成を行った。結果を示す。
wlt_cnn_175_170914.png

Latency は 3.11 ms 程度になってしまった。”dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 をVivado HLS 2017.2 で IP化”では、2.27 ms 程度だったので、Latency が増えてしまった。
wlt_cnn_176_170914.png 

リソース使用量も”dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 をVivado HLS 2017.2 で IP化”よりもFF とLUT が多少増えている。

Export RTL を行った。
wlt_cnn_182_170916.png

結果はCP achiveved post-implementation が 7.288 ns だった。これでマージン的には十分ではないか?と思う。

ZYBO_0_172_8 フォルダ内のstraight_conv_nn2_axis2 フォルダの内容を新しいIP と入れ替えた。
IP Status を表示すると、straight_conv_nn2_axis2_0 が更新されているのが表示されるので、Upgrade Selected ボタンをクリックして、IP Status をアップグレードした。
wlt_cnn_178_170914.png

アップグレードを行った後で、もう一度、論理合成、インプリメント、ビットストリームの生成を行った。
結果を示す。
wlt_cnn_180_170915.png

タイミング制約もメットした。これで問題ないだろう。
  1. 2017年09月16日 04:33 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

白線追従走行用畳み込みニューラルネットワーク・システムの製作1

dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 をVivado HLS 2017.2 で IP化”でIP が揃ったので、白線追従走行用畳み込みニューラルネットワーク・システムの製作を行うことにした。
これは、白線追従走行用畳み込みニューラルネットワークを搭載して、カメラ画像から進む方向(左旋回、直進、右旋回)を決定し、モーターを駆動して、ミニ・ロボットカーに白線間を走行させるシステムである。

さて、今まで作ってきたミニ・ロボットカーのシステムを使用して改造していこう。
Zybot に超音波距離センサを搭載する7(ZYBO_0_162_7 に 2 つのIP を追加)”で使っていたZYBO_0_162_7 を Vivado 2017.2 に変換した ZYBO_0_172_7 をコピーして ZYBO_0_172_8 とした。
wlt_cnn_166_170914.png

ZYBO_0_172_8 フォルダに各IP のフォルダを作成して、そのフォルダ内にIP のファイル(solution1/impl/ip フォルダのZIP ファイルの中身)をコピー&ペーストした。
wlt_cnn_169_170914.png

ZYBO_0_172_8 フォルダの ZYBO_0_153 プロジェクトを立ち上げた。
wlt_cnn_179_170915.png

最初に、IP Catalog にdmar4resize_gray, resize_gray, straight_conv_nn2_axis2 を登録しよう。
左のFlow Navigator のIP Catalog をクリックして、IP Catalog を開く。
IP Catalog 内で右クリックし、右クリックメニューから、Add Repository... を選択し、各IP のフォルダを指定して、IP を追加した。
wlt_cnn_170_170914.png

Gabor filter はDSPをたくさん使用しているので、置いておくとリソース使用量がオーバーしてしまうため、削除する。
wlt_cnn_167_170914.png

Gabor filter を削除した後は、AXI4 Stream のポート間を配線でつないでおく。
wlt_cnn_168_170914.png

dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 のIP をAdd IP し、Run Block Automation を使用して自動配線を行った。出来上がりを下の図に示す。
wlt_cnn_171_170914.png

Validate Design をやったみたところ、成功したので、ブロックデザインは完成したようだ。
全体のブロックデザインはこれだ。
wlt_cnn_181_170915.png

アドレス・エディタの内容を示す。
wlt_cnn_172_170914.png

さて、ブロックデザインが完成したので、論理合成、インプリメント、ビットストリームの生成を行った。
結果を示す。
wlt_cnn_173_170914.png

タイミングエラーだった。

Flow Navigator のOpen Implemented Design をクリックして、Implemented Design を開く。
Timing Summary を見ると、Intra-Clock Paths の clk_fpga_0 でエラーが発生している。
From 、To 共にstraight_conv_nn2_axis2_0 でタイミングエラーが発生している。これは、straight_conv_nn2_axis2 のFF 間のディレイを短くする必要があるようだ。
wlt_cnn_174_170914.png

このようにVivado HLSでPlace and Route までやって、うまく行くと言われてもダメな場合がある。いろいろなIP が入ってきて、タイミング制約を満たすために、あちらは立ってもこちらが立たない状況に追い込まれるからだと考えられる。
次は、Vivado HLSでstraight_conv_nn2_axis2 のClock Period を変更して、再度合成してみよう。
  1. 2017年09月15日 05:12 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 をVivado HLS 2017.2 で IP化

今まで作ってきたIP のdmar4resize_gray, resize_gray, straight_conv_nn2_axis2 は今まで、Vivado HLS 2016.4 を使用してきたが、Vivado HLS 2017.2 で IP化しようと思う。やはり、ZYBOのシステムはVivado 2017.2 で構築するので、その方が良いだろうと思う。

まずは、dmar4resize_gray から、まずはC コードの合成を行った。
wlt_cnn_157_170914.png
wlt_cnn_158_170914.png
問題なさそうだ。

Export RTL を行った。
wlt_cnn_159_170914.png


resize_gray、C コードの合成を行った。
wlt_cnn_160_170914.png
wlt_cnn_161_170914.png

Export RTL を行った。
wlt_cnn_162_170914.png


straight_conv_nn2_axis2、C コードの合成を行った。
wlt_cnn_163_170914.png
wlt_cnn_164_170914.png

Export RTL を行った。
wlt_cnn_165_170914.png

これで、dmar4resize_gray, resize_gray, straight_conv_nn2_axis2 のVivado HLS 2017.2 のIP ができたので、ZYBO のシステムに載せてシステムを構築することができるようになった。
  1. 2017年09月14日 05:17 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

AXI4 Stream版白線追従走行用畳み込みニューラルネットワークIPその3(C シミュレーション、合成)

AXI4 Stream版白線追従走行用畳み込みニューラルネットワークIPその2(C シミュレーション2)”の続き。

前回は、AXI4 Stream のビットアサインがBGR になってしまっていて、RGB では無かったので書き直した。また、畳み込みニューラルネットワークの最後の全結合層の出力をIP の出力として返していたが、分かりにくいので、進行方向の番号を返すように変更した。ちなみに 0 - 左旋回、1 - 直進、2 - 右旋回ということだったが、右旋回の出力が15個の内の5個間違っていた。それで、”CNNのVivado HLS実装のstraight_conv_nn2 の演算精度を変更する”で演算のビット幅を変更した。今回はどの程度右旋回のエラーが直っているか?を見てみよう。

まずは、このプロジェクトでも演算のビット幅を 3 ビット増やした。

ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];


これで C シミュレーションを行った。
wlt_cnn_150_170913.png

右旋回のエラーは 1 個に減った。ログを貼っておく。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
make: 'csim.exe' は更新済みです.
*straight0.bmp
outs = 1
*straight1.bmp
outs = 1
*straight2.bmp
outs = 1
*straight3.bmp
outs = 1
*straight4.bmp
outs = 1
*straight5.bmp
outs = 1
*straight6.bmp
outs = 1
*straight7.bmp
outs = 1
*straight8.bmp
outs = 1
*straight9.bmp
outs = 1
*straight10.bmp
outs = 1
*left_turn0.bmp
outs = 0
*left_turn1.bmp
outs = 0
*left_turn2.bmp
outs = 0
*left_turn3.bmp
outs = 0
*left_turn4.bmp
outs = 0
*left_turn5.bmp
outs = 0
*left_turn6.bmp
outs = 0
*left_turn7.bmp
outs = 0
*left_turn8.bmp
outs = 0
*left_turn9.bmp
outs = 0
*left_turn10.bmp
outs = 0
*right_turn0.bmp
outs = 2
*right_turn1.bmp
outs = 2
*right_turn2.bmp
outs = 2
*right_turn3.bmp
outs = 2
*right_turn4.bmp
outs = 2
*right_turn5.bmp
outs = 2
*right_turn6.bmp
outs = 1
*right_turn7.bmp
outs = 2
*right_turn8.bmp
outs = 2
*right_turn9.bmp
outs = 2
*right_turn10.bmp
outs = 2
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


これで、問題ないと思う。

次に、C コードの合成結果を貼っておく。
wlt_cnn_151_170913.png
wlt_cnn_152_170913.png

Latency は 194426 クロックで、100 MHz 動作では、1.94 ms ということになる。
リソース使用量も問題ないと思う。

C/RTL協調シミュレーションを行った。
wlt_cnn_153_170913.png

Latency は 194463 クロックだった。

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

最初のCNNの判定部分の波形を示す。
wlt_cnn_156_170913.png
カーソルの0x1c 番地のAXI4 Lite Read でCNN の出力が出ていることを確認した。0x18 番地を読んで出力が 1 だということが分かった。次に 0x00 番地を読んで、値が 6 なので、ap_done と ap_idle が立っていることが分かる。
レジスタのアドレス・マップを貼っておく。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of outs_V
//        bit 1~0 - outs_V[1:0] (Read)
//        others  - reserved
// 0x1c : Control signal of outs_V
//        bit 0  - outs_V_ap_vld (Read/COR)
//        others - reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


Export RTL を行った。
wlt_cnn_155_170913.png

これも問題ないようだ。
最後に、straight_conv_nn2_axis2.cpp を貼っておく。

// straight_conv_nn2_axis2.cpp
// 2017/09/09 by marsee
// 畳み込み層のカーネル数 2
// AXI4 Stream入力 番号出力
//

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

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#define REDUSED_ROW        45
#define REDUSED_COULMN    60
#define NUM_OF_KERNELS    2
#define COULMN_PIXELS    56
#define ROW_PIXELS        10
#define ALL_PIXELS        560
#define NUM_OF_OUTPUT    3

int max_ap_fixed(ap_fixed<167, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num);

int straight_conv_nn2_axis2(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=outs
#pragma HLS INTERFACE axis register both port=ins
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<136, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<136, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<167, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_fixed<167, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];
    ap_axiu<32,1,1,1> pix;

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

    // 10 x 56 に整形
    buf_copy1: for(int i=0; i<REDUSED_ROW; i++){
        buf_copy2: for(int j=0; j<REDUSED_COULMN; j++){
            if (!(i==0 && j==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            if((i>=33 && i<33+ROW_PIXELS) && (j>=2 && j<2+COULMN_PIXELS)){
                buf[i-33][j-2] = (ap_ufixed<80, AP_TRN_ZERO, AP_SAT>)((ap_ufixed<168, AP_TRN_ZERO, AP_SAT>)(pix.data & 0xff) / 256);
            }
        }
    }

    // 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++){
                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){
                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++){
                    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++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];
    }

    max_ap_fixed(dot2, outs);

    return(0);
}

int max_ap_fixed(ap_fixed<167, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num){
    int max_id;
    ap_fixed<167, AP_TRN_ZERO, AP_SAT> max;

    for(int i=0; i<NUM_OF_OUTPUT; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    out_num = (ap_uint<2>)max_id;

    return(0);
}


  1. 2017年09月13日 04:23 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

CNNのVivado HLS実装のstraight_conv_nn2 の演算精度を変更する

CNNのVivado HLS実装のstraight_conv_nn2 を再度C シミュレーション”の続き。

前回は、straight_conv_nn2 の精度が良かったのは画像が良かったからという結論が出たので、精度の悪そうな画像でもう一度、straight_conv_nn2 のC シミュレーションを行った。そうすると、ハードウェアの精度は56.7 % だった。これでは精度が悪すぎるので、演算の精度、つまりビット幅を見直すことにした。演算のビット幅を変えながら誤差を見ていこう。

今の演算のビット幅は、以下の通りだ。

ap_fixed<10, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
ap_fixed<10, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
ap_fixed<13, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
ap_fixed<13, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];


畳み込み層の演算のビット幅は、10ビット幅で整数部が6ビット、全結合層の演算のビット幅は、13ビットで整数部が7ビットだ。
これを1ビットずつ増やしてみよう。そして、エラーの数を数えるコードをテストベンチに追加した。
最初に今の演算のビット幅でもう一度、C シミュレーションを行った。
wlt_cnn_148_170911.png
ハードウェアのエラーの個数 hw_err_count は 65 個で、ソフトウェアのエラーの個数 sw_err_count は 17 個だ。
ログを貼っておく。

id = 0, max_id_ref = 1, max_id_hw = 2
id = 0, max_id_ref = 1, max_id_sw = 2
id = 1, max_id_ref = 1, max_id_hw = 2
id = 1, max_id_ref = 1, max_id_sw = 2
id = 5, max_id_ref = 1, max_id_hw = 2
id = 5, max_id_ref = 1, max_id_sw = 2
id = 6, max_id_ref = 1, max_id_hw = 2
id = 6, max_id_ref = 1, max_id_sw = 2
id = 10, max_id_ref = 1, max_id_hw = 2
id = 10, max_id_ref = 1, max_id_sw = 2
id = 15, max_id_ref = 1, max_id_sw = 2
id = 26, max_id_ref = 1, max_id_hw = 0
id = 27, max_id_ref = 1, max_id_hw = 0
id = 28, max_id_ref = 1, max_id_hw = 0
id = 29, max_id_ref = 1, max_id_hw = 0
id = 32, max_id_ref = 1, max_id_hw = 0
id = 33, max_id_ref = 1, max_id_hw = 0
id = 34, max_id_ref = 1, max_id_hw = 0
id = 38, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_sw = 0
id = 43, max_id_ref = 1, max_id_hw = 0
id = 44, max_id_ref = 1, max_id_hw = 0
id = 49, max_id_ref = 1, max_id_hw = 0
id = 80, max_id_ref = 0, max_id_sw = 2
id = 85, max_id_ref = 0, max_id_sw = 2
id = 86, max_id_ref = 0, max_id_sw = 2
id = 90, max_id_ref = 0, max_id_sw = 2
id = 91, max_id_ref = 0, max_id_sw = 2
id = 95, max_id_ref = 0, max_id_sw = 2
id = 96, max_id_ref = 0, max_id_sw = 2
id = 97, max_id_ref = 0, max_id_sw = 2
id = 98, max_id_ref = 0, max_id_sw = 2
id = 99, max_id_ref = 0, max_id_sw = 2
id = 100, max_id_ref = 2, max_id_hw = 0
id = 101, max_id_ref = 2, max_id_hw = 0
id = 102, max_id_ref = 2, max_id_hw = 0
id = 103, max_id_ref = 2, max_id_hw = 0
id = 104, max_id_ref = 2, max_id_hw = 0
id = 105, max_id_ref = 2, max_id_hw = 0
id = 106, max_id_ref = 2, max_id_hw = 0
id = 107, max_id_ref = 2, max_id_hw = 0
id = 108, max_id_ref = 2, max_id_hw = 0
id = 109, max_id_ref = 2, max_id_hw = 0
id = 110, max_id_ref = 2, max_id_hw = 0
id = 111, max_id_ref = 2, max_id_hw = 0
id = 112, max_id_ref = 2, max_id_hw = 0
id = 113, max_id_ref = 2, max_id_hw = 0
id = 114, max_id_ref = 2, max_id_hw = 0
id = 115, max_id_ref = 2, max_id_hw = 0
id = 116, max_id_ref = 2, max_id_hw = 0
id = 117, max_id_ref = 2, max_id_hw = 0
id = 118, max_id_ref = 2, max_id_hw = 0
id = 120, max_id_ref = 2, max_id_hw = 0
id = 121, max_id_ref = 2, max_id_hw = 0
id = 122, max_id_ref = 2, max_id_hw = 0
id = 123, max_id_ref = 2, max_id_hw = 0
id = 125, max_id_ref = 2, max_id_hw = 0
id = 126, max_id_ref = 2, max_id_hw = 0
id = 127, max_id_ref = 2, max_id_hw = 0
id = 128, max_id_ref = 2, max_id_hw = 0
id = 129, max_id_ref = 2, max_id_hw = 0
id = 130, max_id_ref = 2, max_id_hw = 0
id = 131, max_id_ref = 2, max_id_hw = 0
id = 132, max_id_ref = 2, max_id_hw = 0
id = 133, max_id_ref = 2, max_id_hw = 0
id = 134, max_id_ref = 2, max_id_hw = 0
id = 135, max_id_ref = 2, max_id_hw = 0
id = 136, max_id_ref = 2, max_id_hw = 0
id = 137, max_id_ref = 2, max_id_hw = 0
id = 138, max_id_ref = 2, max_id_hw = 0
id = 139, max_id_ref = 2, max_id_hw = 0
id = 140, max_id_ref = 2, max_id_hw = 0
id = 141, max_id_ref = 2, max_id_hw = 0
id = 142, max_id_ref = 2, max_id_hw = 0
id = 143, max_id_ref = 2, max_id_hw = 0
id = 144, max_id_ref = 2, max_id_hw = 0
id = 145, max_id_ref = 2, max_id_hw = 0
id = 146, max_id_ref = 2, max_id_hw = 0
id = 147, max_id_ref = 2, max_id_hw = 0
id = 148, max_id_ref = 2, max_id_hw = 0
id = 149, max_id_ref = 2, max_id_hw = 0
hw_err_count = 65
sw_err_count = 17



次に、畳み込み層と全結合層の演算のビット幅を 1 ビット増やしてみよう。

ap_fixed<11, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
ap_fixed<11, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
ap_fixed<14, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
ap_fixed<14, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];


これでC シミュレーションを行った。
wlt_cnn_149_170911.png
ハードウェアのエラーの個数は 45 個に減少した。
ログを貼っておく。

id = 0, max_id_ref = 1, max_id_hw = 2
id = 0, max_id_ref = 1, max_id_sw = 2
id = 1, max_id_ref = 1, max_id_hw = 2
id = 1, max_id_ref = 1, max_id_sw = 2
id = 5, max_id_ref = 1, max_id_hw = 2
id = 5, max_id_ref = 1, max_id_sw = 2
id = 6, max_id_ref = 1, max_id_sw = 2
id = 10, max_id_ref = 1, max_id_hw = 2
id = 10, max_id_ref = 1, max_id_sw = 2
id = 15, max_id_ref = 1, max_id_sw = 2
id = 28, max_id_ref = 1, max_id_hw = 0
id = 29, max_id_ref = 1, max_id_hw = 0
id = 33, max_id_ref = 1, max_id_hw = 0
id = 34, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_sw = 0
id = 44, max_id_ref = 1, max_id_hw = 0
id = 80, max_id_ref = 0, max_id_sw = 2
id = 85, max_id_ref = 0, max_id_sw = 2
id = 86, max_id_ref = 0, max_id_sw = 2
id = 90, max_id_ref = 0, max_id_sw = 2
id = 91, max_id_ref = 0, max_id_sw = 2
id = 95, max_id_ref = 0, max_id_sw = 2
id = 96, max_id_ref = 0, max_id_sw = 2
id = 97, max_id_ref = 0, max_id_sw = 2
id = 98, max_id_ref = 0, max_id_sw = 2
id = 99, max_id_ref = 0, max_id_sw = 2
id = 101, max_id_ref = 2, max_id_hw = 0
id = 102, max_id_ref = 2, max_id_hw = 0
id = 103, max_id_ref = 2, max_id_hw = 0
id = 104, max_id_ref = 2, max_id_hw = 0
id = 106, max_id_ref = 2, max_id_hw = 0
id = 107, max_id_ref = 2, max_id_hw = 0
id = 108, max_id_ref = 2, max_id_hw = 0
id = 110, max_id_ref = 2, max_id_hw = 0
id = 111, max_id_ref = 2, max_id_hw = 0
id = 112, max_id_ref = 2, max_id_hw = 0
id = 116, max_id_ref = 2, max_id_hw = 0
id = 125, max_id_ref = 2, max_id_hw = 0
id = 126, max_id_ref = 2, max_id_hw = 0
id = 127, max_id_ref = 2, max_id_hw = 0
id = 128, max_id_ref = 2, max_id_hw = 0
id = 129, max_id_ref = 2, max_id_hw = 0
id = 130, max_id_ref = 2, max_id_hw = 0
id = 131, max_id_ref = 2, max_id_hw = 0
id = 132, max_id_ref = 2, max_id_hw = 0
id = 133, max_id_ref = 2, max_id_hw = 0
id = 134, max_id_ref = 2, max_id_hw = 0
id = 135, max_id_ref = 2, max_id_hw = 0
id = 136, max_id_ref = 2, max_id_hw = 0
id = 137, max_id_ref = 2, max_id_hw = 0
id = 138, max_id_ref = 2, max_id_hw = 0
id = 139, max_id_ref = 2, max_id_hw = 0
id = 140, max_id_ref = 2, max_id_hw = 0
id = 141, max_id_ref = 2, max_id_hw = 0
id = 142, max_id_ref = 2, max_id_hw = 0
id = 143, max_id_ref = 2, max_id_hw = 0
id = 144, max_id_ref = 2, max_id_hw = 0
id = 146, max_id_ref = 2, max_id_hw = 0
id = 147, max_id_ref = 2, max_id_hw = 0
id = 148, max_id_ref = 2, max_id_hw = 0
id = 149, max_id_ref = 2, max_id_hw = 0
hw_err_count = 45
sw_err_count = 17


ハードウェアのエラーの個数は 45 個に減少した。


畳み込み層と全結合層の演算のビット幅を 2 ビット増やしてみた。

ap_fixed<12, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
ap_fixed<12, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
ap_fixed<15, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
ap_fixed<15, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];


C シミュレーションを行った。
ログを貼っておく。

id = 0, max_id_ref = 1, max_id_hw = 2
id = 0, max_id_ref = 1, max_id_sw = 2
id = 1, max_id_ref = 1, max_id_hw = 2
id = 1, max_id_ref = 1, max_id_sw = 2
id = 5, max_id_ref = 1, max_id_hw = 2
id = 5, max_id_ref = 1, max_id_sw = 2
id = 6, max_id_ref = 1, max_id_hw = 2
id = 6, max_id_ref = 1, max_id_sw = 2
id = 10, max_id_ref = 1, max_id_hw = 2
id = 10, max_id_ref = 1, max_id_sw = 2
id = 15, max_id_ref = 1, max_id_sw = 2
id = 29, max_id_ref = 1, max_id_hw = 0
id = 34, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_sw = 0
id = 44, max_id_ref = 1, max_id_hw = 0
id = 80, max_id_ref = 0, max_id_sw = 2
id = 85, max_id_ref = 0, max_id_sw = 2
id = 86, max_id_ref = 0, max_id_sw = 2
id = 90, max_id_ref = 0, max_id_sw = 2
id = 91, max_id_ref = 0, max_id_sw = 2
id = 95, max_id_ref = 0, max_id_sw = 2
id = 96, max_id_ref = 0, max_id_sw = 2
id = 97, max_id_ref = 0, max_id_sw = 2
id = 98, max_id_ref = 0, max_id_sw = 2
id = 99, max_id_ref = 0, max_id_sw = 2
id = 102, max_id_ref = 2, max_id_hw = 0
id = 103, max_id_ref = 2, max_id_hw = 0
id = 106, max_id_ref = 2, max_id_hw = 0
id = 107, max_id_ref = 2, max_id_hw = 0
id = 108, max_id_ref = 2, max_id_hw = 0
id = 111, max_id_ref = 2, max_id_hw = 0
id = 112, max_id_ref = 2, max_id_hw = 0
id = 125, max_id_ref = 2, max_id_hw = 0
id = 126, max_id_ref = 2, max_id_hw = 0
id = 127, max_id_ref = 2, max_id_hw = 0
id = 128, max_id_ref = 2, max_id_hw = 0
id = 129, max_id_ref = 2, max_id_hw = 0
id = 130, max_id_ref = 2, max_id_hw = 0
id = 131, max_id_ref = 2, max_id_hw = 0
id = 132, max_id_ref = 2, max_id_hw = 0
id = 133, max_id_ref = 2, max_id_hw = 0
id = 134, max_id_ref = 2, max_id_hw = 0
id = 135, max_id_ref = 2, max_id_hw = 0
id = 136, max_id_ref = 2, max_id_hw = 0
id = 137, max_id_ref = 2, max_id_hw = 0
id = 138, max_id_ref = 2, max_id_hw = 0
id = 139, max_id_ref = 2, max_id_hw = 0
id = 140, max_id_ref = 2, max_id_hw = 0
id = 141, max_id_ref = 2, max_id_hw = 0
id = 142, max_id_ref = 2, max_id_hw = 0
id = 143, max_id_ref = 2, max_id_hw = 0
id = 144, max_id_ref = 2, max_id_hw = 0
id = 146, max_id_ref = 2, max_id_hw = 0
id = 147, max_id_ref = 2, max_id_hw = 0
id = 148, max_id_ref = 2, max_id_hw = 0
id = 149, max_id_ref = 2, max_id_hw = 0
hw_err_count = 40
sw_err_count = 17


ハードウェアのエラーの個数は 40 個に減少した。


畳み込み層と全結合層の演算のビット幅を 3 ビット増やしてみた。

ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];


C シミュレーションを行った。
ログを貼っておく。

id = 0, max_id_ref = 1, max_id_hw = 2
id = 0, max_id_ref = 1, max_id_sw = 2
id = 1, max_id_ref = 1, max_id_hw = 2
id = 1, max_id_ref = 1, max_id_sw = 2
id = 5, max_id_ref = 1, max_id_hw = 2
id = 5, max_id_ref = 1, max_id_sw = 2
id = 6, max_id_ref = 1, max_id_hw = 2
id = 6, max_id_ref = 1, max_id_sw = 2
id = 10, max_id_ref = 1, max_id_hw = 2
id = 10, max_id_ref = 1, max_id_sw = 2
id = 15, max_id_ref = 1, max_id_sw = 2
id = 29, max_id_ref = 1, max_id_hw = 0
id = 34, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_sw = 0
id = 44, max_id_ref = 1, max_id_hw = 0
id = 80, max_id_ref = 0, max_id_sw = 2
id = 85, max_id_ref = 0, max_id_sw = 2
id = 86, max_id_ref = 0, max_id_sw = 2
id = 90, max_id_ref = 0, max_id_sw = 2
id = 91, max_id_ref = 0, max_id_sw = 2
id = 95, max_id_ref = 0, max_id_sw = 2
id = 96, max_id_ref = 0, max_id_sw = 2
id = 97, max_id_ref = 0, max_id_sw = 2
id = 98, max_id_ref = 0, max_id_sw = 2
id = 99, max_id_ref = 0, max_id_sw = 2
id = 102, max_id_ref = 2, max_id_hw = 0
id = 107, max_id_ref = 2, max_id_hw = 0
id = 126, max_id_ref = 2, max_id_hw = 0
id = 127, max_id_ref = 2, max_id_hw = 0
id = 128, max_id_ref = 2, max_id_hw = 0
id = 129, max_id_ref = 2, max_id_hw = 0
id = 131, max_id_ref = 2, max_id_hw = 0
id = 132, max_id_ref = 2, max_id_hw = 0
id = 133, max_id_ref = 2, max_id_hw = 0
id = 134, max_id_ref = 2, max_id_hw = 0
id = 136, max_id_ref = 2, max_id_hw = 0
id = 137, max_id_ref = 2, max_id_hw = 0
id = 138, max_id_ref = 2, max_id_hw = 0
id = 139, max_id_ref = 2, max_id_hw = 0
id = 142, max_id_ref = 2, max_id_hw = 0
id = 143, max_id_ref = 2, max_id_hw = 0
id = 144, max_id_ref = 2, max_id_hw = 0
id = 147, max_id_ref = 2, max_id_hw = 0
id = 148, max_id_ref = 2, max_id_hw = 0
hw_err_count = 28
sw_err_count = 17


ハードウェアのエラーの個数は 28 個に減少した。


畳み込み層と全結合層の演算のビット幅を 4 ビット増やしてみた。

ap_fixed<14, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
ap_fixed<14, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
ap_fixed<17, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
ap_fixed<17, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];


C シミュレーションを行った。
ログを貼っておく。

id = 0, max_id_ref = 1, max_id_hw = 2
id = 0, max_id_ref = 1, max_id_sw = 2
id = 1, max_id_ref = 1, max_id_hw = 2
id = 1, max_id_ref = 1, max_id_sw = 2
id = 5, max_id_ref = 1, max_id_hw = 2
id = 5, max_id_ref = 1, max_id_sw = 2
id = 6, max_id_ref = 1, max_id_hw = 2
id = 6, max_id_ref = 1, max_id_sw = 2
id = 10, max_id_ref = 1, max_id_hw = 2
id = 10, max_id_ref = 1, max_id_sw = 2
id = 15, max_id_ref = 1, max_id_sw = 2
id = 34, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_sw = 0
id = 80, max_id_ref = 0, max_id_sw = 2
id = 85, max_id_ref = 0, max_id_sw = 2
id = 86, max_id_ref = 0, max_id_sw = 2
id = 90, max_id_ref = 0, max_id_sw = 2
id = 91, max_id_ref = 0, max_id_sw = 2
id = 95, max_id_ref = 0, max_id_sw = 2
id = 96, max_id_ref = 0, max_id_sw = 2
id = 97, max_id_ref = 0, max_id_sw = 2
id = 98, max_id_ref = 0, max_id_sw = 2
id = 99, max_id_ref = 0, max_id_sw = 2
id = 102, max_id_ref = 2, max_id_hw = 0
id = 107, max_id_ref = 2, max_id_hw = 0
id = 126, max_id_ref = 2, max_id_hw = 0
id = 127, max_id_ref = 2, max_id_hw = 0
id = 128, max_id_ref = 2, max_id_hw = 0
id = 129, max_id_ref = 2, max_id_hw = 0
id = 131, max_id_ref = 2, max_id_hw = 0
id = 132, max_id_ref = 2, max_id_hw = 0
id = 133, max_id_ref = 2, max_id_hw = 0
id = 134, max_id_ref = 2, max_id_hw = 0
id = 136, max_id_ref = 2, max_id_hw = 0
id = 137, max_id_ref = 2, max_id_hw = 0
id = 138, max_id_ref = 2, max_id_hw = 0
id = 139, max_id_ref = 2, max_id_hw = 0
id = 142, max_id_ref = 2, max_id_hw = 0
id = 143, max_id_ref = 2, max_id_hw = 0
id = 144, max_id_ref = 2, max_id_hw = 0
id = 147, max_id_ref = 2, max_id_hw = 0
id = 148, max_id_ref = 2, max_id_hw = 0
id = 149, max_id_ref = 2, max_id_hw = 0
hw_err_count = 27
sw_err_count = 17


ハードウェアのエラーの個数は 27 個だった。
ここから演算のビット幅を増やしたときのエラーの減少は少なくなっているので、実験をやめることにした。
結局、演算のビット幅を 3 ビット増やした時でやってみることにした。ap_fixedの宣言は以下の通り。

ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];


ここでの精度を計算してみよう。
ハードウェアのエラーの個数は 28 個なので、(150 - 28) / 150 x 100 ≒ 81.3 %
ソフトウェアのエラーの個数は 17 個なので、(150 - 17) / 150 x 100 ≒ 88.7 %
となった。

最後に straight_conv_nn_tb.cpp を貼っておく。

// straight_conv_nn_tb.cpp
// 2017/08/28 by marsee
// 畳み込み層のカーネル数 2
//

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

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#include "straight_data.h"

#define NUM_OF_KERNELS 2
#define COULMN_PIXELS 56
#define ROW_PIXELS 10
#define ALL_PIXELS 560
#define NUM_OF_OUTPUT 3

int straight_conv_nn(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[ALL_PIXELS], ap_fixed<137, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT]);
int straight_conv_nn_float(float in[ALL_PIXELS], float out[NUM_OF_OUTPUT]);
int max_ap_fixed(ap_fixed<137, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT]);
int max_float(float out[NUM_OF_OUTPUT]);

#define NUM_ITERATIONS    150 // C Simulation
//#define NUM_ITERATIONS    2 // C/RTL CoSimulation

int main(){
    float t_tran_float[NUM_ITERATIONS][ALL_PIXELS];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> result_ap_fixed[NUM_ITERATIONS][NUM_OF_OUTPUT];
    float result_float[NUM_ITERATIONS][NUM_OF_OUTPUT];
    int max_id_hw, max_id_sw, max_id_ref;

    for(int i=0; i<NUM_ITERATIONS; i++)
        for(int j=0; j<ALL_PIXELS; j++)
            t_tran_float[i][j] = (float)t_train[i][j];

    for(int i=0; i<NUM_ITERATIONS; i++){
        straight_conv_nn(&t_train[i][0], &result_ap_fixed[i][0]);
        straight_conv_nn_float(&t_tran_float[i][0], &result_float[i][0]);
    }

    int hw_err_count=0;
    int sw_err_count=0;
    for(int i=0; i<NUM_ITERATIONS; i++){
        max_id_hw = max_ap_fixed(&result_ap_fixed[i][0]);
        max_id_sw = max_float(&result_float[i][0]);
        max_id_ref = max_float(&t_test[i][0]);

        if(max_id_ref != max_id_hw){
            printf("id = %d, max_id_ref = %d, max_id_hw = %d\n", i, max_id_ref, max_id_hw);
            hw_err_count++;
        }
        if(max_id_ref != max_id_sw){
            printf("id = %d, max_id_ref = %d, max_id_sw = %d\n", i, max_id_ref, max_id_sw);
            sw_err_count++;
        }
    }
    if(hw_err_count==0 && sw_err_count==0)
        printf("No Error\n");
    else{
        printf("hw_err_count = %d\n", hw_err_count);
        printf("sw_err_count = %d\n", sw_err_count);
    }

    return(0);
}

int straight_conv_nn_float(float in[ALL_PIXELS], float out[NUM_OF_OUTPUT]){
    float buf[ROW_PIXELS][COULMN_PIXELS];
    float conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    float pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    float dot1[100];
    float dot2[NUM_OF_OUTPUT];

    buf_copy1: for(int i=0; i<ROW_PIXELS; i++)
        buf_copy2: for(int j=0; j<COULMN_PIXELS; j++)
            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++){
                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_fweight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_fbias[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){
                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++){
                    dot1[col] += pool_out[i][j][k]*af1_fweight[i*((ROW_PIXELS-4)/2)*((COULMN_PIXELS-4)/2)+j*((COULMN_PIXELS-4)/2)+k][col];
                }
            }
        }
        dot1[col] += af1_fbias[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++){
            dot2[col] += dot1[row]*af2_fweight[row][col];
        }
        dot2[col] += af2_fbias[col];

        out[col] = dot2[col];
    }

    return(0);
}

int max_ap_fixed(ap_fixed<137, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT]){
    int max_id;
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> max;

    for(int i=0; i<NUM_OF_OUTPUT; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

int max_float(float out[NUM_OF_OUTPUT]){
    int max_id;
    float max;

    for(int i=0; i<NUM_OF_OUTPUT; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

  1. 2017年09月12日 04:21 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

CNNのVivado HLS実装のstraight_conv_nn2 を再度C シミュレーション

白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)”の続き。

AXI4 Stream版白線追従走行用畳み込みニューラルネットワークIPその2(C シミュレーション2)”で 、”白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)”のstraight_conv_nn2 の精度が良かったのは画像が良かったからという結論が出たので、精度の悪そうな画像でもう一度、straight_conv_nn2 のC シミュレーションを行った。

白線の3つ目と4つ目の白線画像の精度が悪いので、1つの画像を25個に増やしているので、75個目の画像ということになる。Pythonコードを使用して、straight_data.h を再度生成した。
wlt_cnn_144_170911.png

straight_data.h が生成された。
wlt_cnn_145_170911.png

Vivado HLSのstraight_conv_nn2 のstraight_data.h を取り換えた。
wlt_cnn_146_170911.png

そして、C シミュレーションを行った。
wlt_cnn_147_170911.png

結果を貼っておく。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../straight_conv_nn_tb.cpp in debug mode
Generating csim.exe
id = 0, max_id_ref = 1, max_id_hw = 2
id = 0, max_id_ref = 1, max_id_sw = 2
id = 1, max_id_ref = 1, max_id_hw = 2
id = 1, max_id_ref = 1, max_id_sw = 2
id = 5, max_id_ref = 1, max_id_hw = 2
id = 5, max_id_ref = 1, max_id_sw = 2
id = 6, max_id_ref = 1, max_id_hw = 2
id = 6, max_id_ref = 1, max_id_sw = 2
id = 10, max_id_ref = 1, max_id_hw = 2
id = 10, max_id_ref = 1, max_id_sw = 2
id = 15, max_id_ref = 1, max_id_sw = 2
id = 26, max_id_ref = 1, max_id_hw = 0
id = 27, max_id_ref = 1, max_id_hw = 0
id = 28, max_id_ref = 1, max_id_hw = 0
id = 29, max_id_ref = 1, max_id_hw = 0
id = 32, max_id_ref = 1, max_id_hw = 0
id = 33, max_id_ref = 1, max_id_hw = 0
id = 34, max_id_ref = 1, max_id_hw = 0
id = 38, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_hw = 0
id = 39, max_id_ref = 1, max_id_sw = 0
id = 43, max_id_ref = 1, max_id_hw = 0
id = 44, max_id_ref = 1, max_id_hw = 0
id = 49, max_id_ref = 1, max_id_hw = 0
id = 80, max_id_ref = 0, max_id_sw = 2
id = 85, max_id_ref = 0, max_id_sw = 2
id = 86, max_id_ref = 0, max_id_sw = 2
id = 90, max_id_ref = 0, max_id_sw = 2
id = 91, max_id_ref = 0, max_id_sw = 2
id = 95, max_id_ref = 0, max_id_sw = 2
id = 96, max_id_ref = 0, max_id_sw = 2
id = 97, max_id_ref = 0, max_id_sw = 2
id = 98, max_id_ref = 0, max_id_sw = 2
id = 99, max_id_ref = 0, max_id_sw = 2
id = 100, max_id_ref = 2, max_id_hw = 0
id = 101, max_id_ref = 2, max_id_hw = 0
id = 102, max_id_ref = 2, max_id_hw = 0
id = 103, max_id_ref = 2, max_id_hw = 0
id = 104, max_id_ref = 2, max_id_hw = 0
id = 105, max_id_ref = 2, max_id_hw = 0
id = 106, max_id_ref = 2, max_id_hw = 0
id = 107, max_id_ref = 2, max_id_hw = 0
id = 108, max_id_ref = 2, max_id_hw = 0
id = 109, max_id_ref = 2, max_id_hw = 0
id = 110, max_id_ref = 2, max_id_hw = 0
id = 111, max_id_ref = 2, max_id_hw = 0
id = 112, max_id_ref = 2, max_id_hw = 0
id = 113, max_id_ref = 2, max_id_hw = 0
id = 114, max_id_ref = 2, max_id_hw = 0
id = 115, max_id_ref = 2, max_id_hw = 0
id = 116, max_id_ref = 2, max_id_hw = 0
id = 117, max_id_ref = 2, max_id_hw = 0
id = 118, max_id_ref = 2, max_id_hw = 0
id = 120, max_id_ref = 2, max_id_hw = 0
id = 121, max_id_ref = 2, max_id_hw = 0
id = 122, max_id_ref = 2, max_id_hw = 0
id = 123, max_id_ref = 2, max_id_hw = 0
id = 125, max_id_ref = 2, max_id_hw = 0
id = 126, max_id_ref = 2, max_id_hw = 0
id = 127, max_id_ref = 2, max_id_hw = 0
id = 128, max_id_ref = 2, max_id_hw = 0
id = 129, max_id_ref = 2, max_id_hw = 0
id = 130, max_id_ref = 2, max_id_hw = 0
id = 131, max_id_ref = 2, max_id_hw = 0
id = 132, max_id_ref = 2, max_id_hw = 0
id = 133, max_id_ref = 2, max_id_hw = 0
id = 134, max_id_ref = 2, max_id_hw = 0
id = 135, max_id_ref = 2, max_id_hw = 0
id = 136, max_id_ref = 2, max_id_hw = 0
id = 137, max_id_ref = 2, max_id_hw = 0
id = 138, max_id_ref = 2, max_id_hw = 0
id = 139, max_id_ref = 2, max_id_hw = 0
id = 140, max_id_ref = 2, max_id_hw = 0
id = 141, max_id_ref = 2, max_id_hw = 0
id = 142, max_id_ref = 2, max_id_hw = 0
id = 143, max_id_ref = 2, max_id_hw = 0
id = 144, max_id_ref = 2, max_id_hw = 0
id = 145, max_id_ref = 2, max_id_hw = 0
id = 146, max_id_ref = 2, max_id_hw = 0
id = 147, max_id_ref = 2, max_id_hw = 0
id = 148, max_id_ref = 2, max_id_hw = 0
id = 149, max_id_ref = 2, max_id_hw = 0
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


なかなか面白い結果が出た。ちなみに、max_id_sw がハードウェア化の固定小数点数で演算したときの結果で、max_id_sw がソフトウェアの浮動小数点数演算の結果だ。
ソフトウェアが顕著に間違っているのは、max_id_ref = 0, max_id_sw = 2 で左旋回を右旋回と間違っている。ハードウェアが間違ったのが、max_id_ref = 1, max_id_hw = 0 で直進を左旋回と間違っていたのと、max_id_ref = 2, max_id_hw = 0 で、右旋回を左旋回と間違っている。

ソフトウェアの間違いが 17 個、ハードウェアの間違いが 65 個だった。ソフトウェアの精度は (150-17) /150 ≒ 88.7 % で、ハードウェアの精度は (150-65)/150 ≒ 56.7 % だった。

ソフトウェアとハードウェアでここまで、間違いに差があるとはびっくりした。演算の精度を落としたことで、微妙な差が分かりにくくなるということがあるのかもしれない。なお、右旋回と左旋回の白線の画像にはほとんど差が無い場合がある。本来ならば、もう少し、広角のカメラで白線を撮影するか?白線内部の色を変更する必要があるかもしれない。つまり、道路の色の違いを使用する訳だ。今のところは板に白線をテープで貼っただけなのだ。
  1. 2017年09月11日 03:54 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

AXI4 Stream版白線追従走行用畳み込みニューラルネットワークIPその2(C シミュレーション2)

AXI4 Stream版白線追従走行用畳み込みニューラルネットワークIPその1(C シミュレーション)”の続き。

AXI4 Stream版白線追従走行用畳み込みニューラルネットワークIPその1(C シミュレーション)”は、、”Vivado HLS で画像のサイズを縮小して白黒変換2(resize_gray)”のAXI4 Stream 入力を受けてとは書いたが、BGR になってしまっていて、RGB では無かったので書き直した。また、畳み込みニューラルネットワークの最後の全結合層の出力をIP の出力として返していたが、分かりにくいので、進行方向の番号を返すように変更した。ちなみに 0 - 左旋回、1 - 直進、2 - 右旋回だ。

新しく、straight_conv_nn2_axis2 プロジェクトを作成した。
wlt_cnn_142_170910.png

これで、C シミュレーションを行った。
wlt_cnn_143_170910.png

C シミュレーションの出力を示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../straight_conv_nn2_axis2_tb.cpp in debug mode
Generating csim.exe
*straight0.bmp
outs = 1
*straight1.bmp
outs = 1
*straight2.bmp
outs = 1
*straight3.bmp
outs = 1
*straight4.bmp
outs = 1
*straight5.bmp
outs = 1
*straight6.bmp
outs = 1
*straight7.bmp
outs = 1
*straight8.bmp
outs = 1
*straight9.bmp
outs = 1
*straight10.bmp
outs = 1
*left_turn0.bmp
outs = 0
*left_turn1.bmp
outs = 0
*left_turn2.bmp
outs = 0
*left_turn3.bmp
outs = 0
*left_turn4.bmp
outs = 0
*left_turn5.bmp
outs = 0
*left_turn6.bmp
outs = 0
*left_turn7.bmp
outs = 0
*left_turn8.bmp
outs = 0
*left_turn9.bmp
outs = 0
*left_turn10.bmp
outs = 0
*right_turn0.bmp
outs = 2
*right_turn1.bmp
outs = 2
*right_turn2.bmp
outs = 0
*right_turn3.bmp
outs = 0
*right_turn4.bmp
outs = 0
*right_turn5.bmp
outs = 0
*right_turn6.bmp
outs = 1
*right_turn7.bmp
outs = 2
*right_turn8.bmp
outs = 2
*right_turn9.bmp
outs = 2
*right_turn10.bmp
outs = 2
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


やはり右旋回がだいぶ間違っている。あれ?”白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)”では、150 画像で 1 枚しかハードウェアでは間違っていなかったはず?なぜこんな差が出てくるんだろうということで調べてみた。

白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)”では、straight_data.h の画像に対して合っているかを調べていた。
straight_data.h は、”白線追従走行用畳み込みニューラルネットワークの製作16(白線追従走行用の画像データをCの配列に出力)”で作られているが、コードを見ると、トレーニングデータでなくてテストデータで作成されている。

ということで、早速、straight_conv_nn2_axis2 プロジェクトの画像をテストデータに置き換えたところ、下の結果が得られた。

*straight_test0.bmp
outs = 1
*straight_test1.bmp
outs = 1
*straight_test2.bmp
outs = 1
*straight_test3.bmp
outs = 1
*straight_test4.bmp
outs = 1
*left_turn_test0.bmp
outs = 0
*left_turn_test1.bmp
outs = 0
*left_turn_test2.bmp
outs = 0
*left_turn_test3.bmp
outs = 0
*left_turn_test4.bmp
outs = 0
*right_turn_test0.bmp
outs = 2
*right_turn_test1.bmp
outs = 2
*right_turn_test2.bmp
outs = 2
*right_turn_test3.bmp
outs = 0
*right_turn_test4.bmp
outs = 0


やはり右旋回も間違っているが、最初から3つ目までは正しい。

白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)”でテストしていた staright_data.h は左旋回、直進、右旋回とも 50 画像ずつだ。撮影した1つの画像は25個に増やしているので、”白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)”でテストしていた staright_data.h では、2 つの画像しかテストしてないことになる。つまり判定しやすい画像をテストしていた訳である。

straight_conv_nn2_axis2.cpp を貼っておく。

// straight_conv_nn2_axis2.cpp
// 2017/09/09 by marsee
// 畳み込み層のカーネル数 2
// AXI4 Stream入力 番号出力
//

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

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#define REDUSED_ROW        45
#define REDUSED_COULMN    60
#define NUM_OF_KERNELS    2
#define COULMN_PIXELS    56
#define ROW_PIXELS        10
#define ALL_PIXELS        560
#define NUM_OF_OUTPUT    3

int max_ap_fixed(ap_fixed<137, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num);

int straight_conv_nn2_axis2(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=outs
#pragma HLS INTERFACE axis register both port=ins
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];
    ap_axiu<32,1,1,1> pix;

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

    // 10 x 56 に整形
    buf_copy1: for(int i=0; i<REDUSED_ROW; i++){
        buf_copy2: for(int j=0; j<REDUSED_COULMN; j++){
            if (!(i==0 && j==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            if((i>=33 && i<33+ROW_PIXELS) && (j>=2 && j<2+COULMN_PIXELS)){
                buf[i-33][j-2] = (ap_ufixed<80, AP_TRN_ZERO, AP_SAT>)((ap_ufixed<168, AP_TRN_ZERO, AP_SAT>)(pix.data & 0xff) / 256);
            }
        }
    }

    // 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++){
                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){
                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++){
                    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++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];
    }

    max_ap_fixed(dot2, outs);

    return(0);
}

int max_ap_fixed(ap_fixed<137, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num){
    int max_id;
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> max;

    for(int i=0; i<NUM_OF_OUTPUT; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    out_num = (ap_uint<2>)max_id;

    return(0);
}


straight_conv_nn2_axis2_tb.cpp を貼っておく。

// straight_conv_nn2_axis2_tb.cpp
// 2017/09/09 by marsee
//

#include <iostream>
#include "hls_opencv.h"
#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT    600
#define MAX_WIDTH    800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;

using namespace cv;

#define NUM_OF_OUTPUT    3

#define MAX_LOOP_COUNT    11
//#define MAX_LOOP_COUNT    1    // for C/RTL Co-Simulation

#define STRAIGHT_IMAGE_NAME        "straight"
#define LEFT_TURN_IMAGE_NAME    "left_turn"
#define RIGHT_TURN_IMAGE_NAME    "right_turn"

int straight_conv_nn2_axis2(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs);
int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs);
int main_output_loop(char *buf);

int main () {
    char buf[200];

    sprintf(buf, "%s", STRAIGHT_IMAGE_NAME);
    main_output_loop(buf);

    sprintf(buf, "%s", LEFT_TURN_IMAGE_NAME);
    main_output_loop(buf);

    sprintf(buf, "%s", RIGHT_TURN_IMAGE_NAME);
    main_output_loop(buf);

    return(0);
}

int main_output_loop(char *buf){
    char bmp_file_name[200];
    ap_uint<2> outs;
    AXI_STREAM src_axi, dst_axi;
    Mat src;

    for(int i=0; i<MAX_LOOP_COUNT; i++){
        sprintf(bmp_file_name, "%s%d.bmp", buf, i);

        // OpenCV で 画像を読み込む
        src = imread(bmp_file_name);

        // BGR から RGBへ変換
        Mat src_rgb;
        cvtColor(src, src_rgb, CV_BGR2RGB);

        // Mat フォーマットから AXI4 Stream へ変換
        cvMat2AXIvideo(src_rgb, src_axi);

        // resize_gray() 関数をコール
        resize_gray(src_axi, dst_axi);

        straight_conv_nn2_axis2(dst_axi, outs);

        printf("*%s\n", bmp_file_name);
        printf("outs = %d\n", (int)outs);
    }

    return(0);
}

int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs){

    RGB_IMAGE org_img(600800);
    GRAY_IMAGE org_img_g(600800);
    GRAY_IMAGE resize_img_g(4560);
    RGB_IMAGE resize_img(4560);

    hls::AXIvideo2Mat(ins, org_img);
    hls::CvtColor<HLS_RGB2GRAY>(org_img, org_img_g);
    hls::Resize(org_img_g, resize_img_g);
    hls::CvtColor<HLS_GRAY2RGB>(resize_img_g, resize_img);
    hls::Mat2AXIvideo(resize_img, outs);

    return(0);
}

  1. 2017年09月10日 07:48 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

AXI4 Stream版白線追従走行用畳み込みニューラルネットワークIPその1(C シミュレーション)

白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)
白線追従走行用畳み込みニューラルネットワークの製作19(Cコードの合成、IP化)
で作成した白線追従走行用畳み込みニューラルネットワークをAXI4 Streamで入力するように変更した。これは、”Vivado HLS で画像のサイズを縮小して白黒変換2(resize_gray)”のAXI4 Stream 入力を受けて、白線追従走行用畳み込みニューラルネットワークにそのAXI4 Stream データを入力して処理する。

straight_conv_nn2_axis プロジェクトを Ubuntu 16.04 上のVivado HLS 2016.4 で作成した。
wlt_cnn_139_170908.png

C シミュレーションを行った。結果を示す。
wlt_cnn_140_170908.png
出力結果を貼っておく。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../straight_conv_nn2_axis_tb.cpp in debug mode
Generating csim.exe
*straight0.bmp
outs[0] = -6.218750 outs[1] = 3.812500 outs[2] = -3.078125
*straight1.bmp
outs[0] = -5.625000 outs[1] = 2.500000 outs[2] = -1.796875
*straight2.bmp
outs[0] = -3.796875 outs[1] = 4.015625 outs[2] = -5.890625
*straight3.bmp
outs[0] = -3.484375 outs[1] = 0.875000 outs[2] = -2.140625
*straight4.bmp
outs[0] = -0.765625 outs[1] = 2.218750 outs[2] = -6.156250
*straight5.bmp
outs[0] = -5.468750 outs[1] = 3.046875 outs[2] = -2.828125
*straight6.bmp
outs[0] = -6.500000 outs[1] = 5.062500 outs[2] = -4.234375
*straight7.bmp
outs[0] = -5.140625 outs[1] = 2.187500 outs[2] = -1.312500
*straight8.bmp
outs[0] = 1.359375 outs[1] = 1.546875 outs[2] = -8.156250
*straight9.bmp
outs[0] = -5.390625 outs[1] = 2.515625 outs[2] = -1.375000
*straight10.bmp
outs[0] = 1.718750 outs[1] = 1.640625 outs[2] = -8.796875
*left_turn0.bmp
outs[0] = 5.671875 outs[1] = -0.515625 outs[2] = -9.937500
*left_turn1.bmp
outs[0] = 5.093750 outs[1] = -3.734375 outs[2] = -3.078125
*left_turn2.bmp
outs[0] = 6.500000 outs[1] = -6.875000 outs[2] = -0.593750
*left_turn3.bmp
outs[0] = 6.078125 outs[1] = -6.546875 outs[2] = -0.515625
*left_turn4.bmp
outs[0] = 6.984375 outs[1] = -9.109375 outs[2] = 1.859375
*left_turn5.bmp
outs[0] = 7.250000 outs[1] = -9.593750 outs[2] = 2.109375
*left_turn6.bmp
outs[0] = 6.359375 outs[1] = -3.046875 outs[2] = -5.593750
*left_turn7.bmp
outs[0] = 8.843750 outs[1] = -8.890625 outs[2] = -1.203125
*left_turn8.bmp
outs[0] = 8.453125 outs[1] = -8.781250 outs[2] = -0.656250
*left_turn9.bmp
outs[0] = 5.343750 outs[1] = -1.062500 outs[2] = -7.953125
*left_turn10.bmp
outs[0] = 7.609375 outs[1] = -6.062500 outs[2] = -3.421875
*right_turn0.bmp
outs[0] = -1.578125 outs[1] = -0.890625 outs[2] = 0.843750
*right_turn1.bmp
outs[0] = 3.828125 outs[1] = -6.937500 outs[2] = 3.937500
*right_turn2.bmp
outs[0] = 3.296875 outs[1] = -5.375000 outs[2] = 2.562500
*right_turn3.bmp
outs[0] = 2.796875 outs[1] = -4.234375 outs[2] = 1.421875
*right_turn4.bmp
outs[0] = 3.562500 outs[1] = -4.625000 outs[2] = 0.843750
*right_turn5.bmp
outs[0] = 2.890625 outs[1] = -3.906250 outs[2] = 0.328125
*right_turn6.bmp
outs[0] = -2.109375 outs[1] = -0.312500 outs[2] = -1.296875
*right_turn7.bmp
outs[0] = 0.281250 outs[1] = -2.843750 outs[2] = 2.375000
*right_turn8.bmp
outs[0] = 0.671875 outs[1] = -5.812500 outs[2] = 6.656250
*right_turn9.bmp
outs[0] = 1.562500 outs[1] = -4.765625 outs[2] = 3.437500
*right_turn10.bmp
outs[0] = 3.515625 outs[1] = -7.093750 outs[2] = 4.437500
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


right_turn2.bmp、right_turn3.bmp、right_turn4.bmp が左旋回にミスっているが、下の図を見ると左旋回とも取れる画像になっているので仕方ないかもしれない。右旋回のミスは多いかもしれない?
wlt_cnn_141_170908.jpg

right_turn2.bmp、right_turn3.bmp、right_turn4.bmp はどのようなシチュエーションなのかを示す。

2: 角度 -5 度、左車輪逸脱車体1/4
3: 角度 -10 度、左車輪逸脱車体1/4
4: 角度 0 度、左車輪逸脱車体1/2


staight_conv_nn2_axis.cpp を貼っておく。

// straight_conv_nn2_axis.cpp
// 2017/09/05 by marsee
// 畳み込み層のカーネル数 2
// AXI4 Stream入力
//

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

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#define REDUSED_ROW        45
#define REDUSED_COULMN    60
#define NUM_OF_KERNELS    2
#define COULMN_PIXELS    56
#define ROW_PIXELS        10
#define ALL_PIXELS        560
#define NUM_OF_OUTPUT    3

int straight_conv_nn2_axis(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_fixed<137, AP_TRN_ZERO, AP_SAT> outs[NUM_OF_OUTPUT]){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=outs
#pragma HLS INTERFACE axis register both port=ins
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];
    ap_axiu<32,1,1,1> pix;

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

    // 10 x 56 に整形
    buf_copy1: for(int i=0; i<REDUSED_ROW; i++){
        buf_copy2: for(int j=0; j<REDUSED_COULMN; j++){
            if (!(i==0 && j==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            if((i>=33 && i<33+ROW_PIXELS) && (j>=2 && j<2+COULMN_PIXELS)){
                buf[i-33][j-2] = (ap_ufixed<80, AP_TRN_ZERO, AP_SAT>)((ap_ufixed<168, AP_TRN_ZERO, AP_SAT>)(pix.data & 0xff) / 256);
                //printf("%1x", (((unsigned int)pix.data&0xff)+8)/16);
                //if(j==2+COULMN_PIXELS-1)
                //    printf("\n");
            }
        }
    }

    // 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++){
                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){
                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++){
                    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++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];

        outs[col] = dot2[col];
    }

    return(0);
}


staight_conv_nn2_axis_tb.cpp を貼っておく。

// straight_conv_nn2_axis_tb.cpp
// 2017/09/06 by marsee
//

#include <iostream>
#include "hls_opencv.h"
#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT  600
#define MAX_WIDTH   800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;

using namespace cv;

#define NUM_OF_OUTPUT   3

#define MAX_LOOP_COUNT    11
//#define MAX_LOOP_COUNT  1   // for C/RTL Co-Simulation

int straight_conv_nn2_axis(AXI_STREAM& ins, ap_fixed<137, AP_TRN_ZERO, AP_SAT> outs[NUM_OF_OUTPUT]);
int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs);
int main_output_loop(char *buf);

int main () {
    char buf[200];

    sprintf(buf, "%s""straight");
    main_output_loop(buf);

    sprintf(buf, "%s""left_turn");
    main_output_loop(buf);

    sprintf(buf, "%s""right_turn");
    main_output_loop(buf);

    return(0);
}

int main_output_loop(char *buf){
    char bmp_file_name[200];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> outs[NUM_OF_OUTPUT];
    AXI_STREAM src_axi, dst_axi;
    Mat src;

    for(int i=0; i<MAX_LOOP_COUNT; i++){
        sprintf(bmp_file_name, "%s%d.bmp", buf, i);

        // OpenCV で 画像を読み込む
        src = imread(bmp_file_name);

        // Mat フォーマットから AXI4 Stream へ変換
        cvMat2AXIvideo(src, src_axi);

        // resize_gray() 関数をコール
        resize_gray(src_axi, dst_axi);

        straight_conv_nn2_axis(dst_axi, outs);

        printf("*%s\n", bmp_file_name);
        for(int i=0; i<NUM_OF_OUTPUT; i++)
            printf("outs[%d] = %f ", i, (float)outs[i]);
        printf("\n");
    }

    return(0);
}

int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs){

    RGB_IMAGE org_img(600800);
    GRAY_IMAGE org_img_g(600800);
    GRAY_IMAGE resize_img_g(4560);
    RGB_IMAGE resize_img(4560);

    hls::AXIvideo2Mat(ins, org_img);
    hls::CvtColor<HLS_BGR2GRAY>(org_img, org_img_g);
    hls::Resize(org_img_g, resize_img_g);
    hls::CvtColor<HLS_GRAY2BGR>(resize_img_g, resize_img);
    hls::Mat2AXIvideo(resize_img, outs);

    return(0);
}

  1. 2017年09月08日 05:33 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で画像のサイズを縮小して白黒変換2(resize_gray)

Vivado HLS で画像のサイズを縮小して白黒変換(resize_gray)”を少し修正した。resize_grey はOpenCV やHLS ビデオライブラリを使用して画像をリサイズして白黒変換するのだが、その色のビット・フィールドのアサインはBGR になっている。現在の自作カメラ・インターフェイス IP などのハードウェアの色ビットの割り当てはRGB になっているので、色のビット・フィールドを入れ替える必要がある。そのため、resize_gray の色ビット・フィールドをRGB に入れ替えようと思う。

新しい resize_gray.cpp を貼っておく。

// resize_gray.cpp
// 2017/08/31 by marsee
// 2017/09/06 : BRG to RGB
//

#include "resize_gray.h"

int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs){
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS DATAFLOW
#pragma HLS INTERFACE s_axilite port=return

    RGB_IMAGE org_img(600800);
    GRAY_IMAGE org_img_g(600800);
    GRAY_IMAGE resize_img_g(4560);
    RGB_IMAGE resize_img(4560);

    hls::AXIvideo2Mat(ins, org_img);
    hls::CvtColor<HLS_RGB2GRAY>(org_img, org_img_g);
    hls::Resize(org_img_g, resize_img_g);
    hls::CvtColor<HLS_GRAY2RGB>(resize_img_g, resize_img);
    hls::Mat2AXIvideo(resize_img, outs);

    return(0);
}


次に、resize_gray_tb.cpp を貼っておく。

// resize_gray_tb.cpp
// 2017/08/31 by marsee
// 2017/09/06 : BRG to RGB
//

#include <iostream>
#include "hls_opencv.h"
#include "resize_gray.h"

using namespace cv;

#define INPUT_IMAGE        "straight0.bmp"
#define OUTPUT_IMAGE    "test_straight0.bmp"
#define OUTPUT_IMAGE_CV    "test_straight0_cv.bmp"

void resize_gray(AXI_STREAM& ins, AXI_STREAM& outs);
void opencv_resize_gray(Mat& src, Mat& dst);

int main (int argc, char** argv) {
    // OpenCV で 画像を読み込む
    Mat src = imread(INPUT_IMAGE);
    AXI_STREAM src_axi, dst_axi;

    // BGR から RGBへ変換
    Mat src_rgb;
    cvtColor(src, src_rgb, CV_BGR2RGB);

    // Mat フォーマットから AXI4 Stream へ変換
    cvMat2AXIvideo(src_rgb, src_axi);

    // resize_gray() 関数をコール
    resize_gray(src_axi, dst_axi);

    // AXI4 Stream から Mat フォーマットへ変換
    // dst は宣言時にサイズとカラー・フォーマットを定義する必要がある
    Mat dst_rgb(4560, CV_8UC3);
    AXIvideo2cvMat(dst_axi, dst_rgb);
    Mat dst;
    cvtColor(dst_rgb, dst, CV_RGB2BGR);

    // Mat フォーマットからファイルに書き込み
    imwrite(OUTPUT_IMAGE, dst);

    // opencv_resize_gray() をコール
    Mat dst_cv(4560, CV_8UC3);
    opencv_resize_gray(src, dst_cv);
    imwrite(OUTPUT_IMAGE_CV, dst_cv);

    // dst と dst_cv が同じ画像かどうか?比較する
    for (int y=0; y<45; y++){
        Vec3b* dst_ptr = dst.ptr<Vec3b>(y);
        Vec3b* dst_cv_ptr = dst_cv.ptr<Vec3b>(y);
        for (int x=0; x<60; x++){
            Vec3b dst_bgr = dst_ptr[x];
            Vec3b dst_cv_bgr = dst_cv_ptr[x];

            // bgr のどれかが間違っていたらエラー
            if (std::pow(dst_bgr[0]-dst_cv_bgr[0], 2.0) > 1 || std::pow(dst_bgr[1]-dst_cv_bgr[1], 2.0) > 1
                    || std::pow(dst_bgr[2]-dst_cv_bgr[2], 2.0) > 1){
                printf("x = %d, y = %d,  Error dst=%d,%d,%d dst_cv=%d,%d,%d\n", x, y,
                        dst_bgr[0], dst_bgr[1], dst_bgr[0], dst_cv_bgr[0], dst_cv_bgr[1], dst_cv_bgr[2]);
                //return 1;
            }
        }
    }
    printf("Test with 0 errors.\n");

    return 0;
}

void opencv_resize_gray(Mat& src, Mat& dst){
    Mat gray(src.rows, src.cols, CV_8UC1);
    Mat img0g(4560, CV_8UC1);

    cvtColor(src, gray, CV_BGR2GRAY);
    resize(gray, img0g, img0g.size(), 00, INTER_LINEAR);
    cvtColor(img0g, dst, CV_GRAY2BGR);
}


合成結果は、”Vivado HLS で画像のサイズを縮小して白黒変換(resize_gray)”と全く同じだった。

C/RTL協調シミュレーションも”Vivado HLS で画像のサイズを縮小して白黒変換(resize_gray)”と全く同じだったが、Export RTL は値が違っていた。
wlt_cnn_138_170907.png

BGR の時のExport RTL の結果を示す。
wlt_cnn_109_170901.png

RGB の時のほうがSLICE とLUT が増えている。
  1. 2017年09月07日 05:21 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で実装した畳み込みニューラルネットワークの指示子による性能差2

Vivado HLS で実装した畳み込みニューラルネットワークの指示子による性能差”の続き。

前回は、指示子なしの場合の性能とリソース使用量と目いっぱい指示子を入れたときの差を検証した。今回は更に性能が向上したのでご紹介する。

本当は、1クロックで1出力できるのが良いのだが、(何に使うの?というご指摘はもっともだと思うが、それは置いておいて)やってみよう。
conv_out の指示子のARRAY_PARTITION complete dim=1 を dim=3 に変更してみた。dim はどの次元をパーティションするかを支持するオプションで 1 だと 1 次元目、3 だと 3 次元目となる。0 だとすべての次元をバラバラにするのだが、これをしているとコンパイルに時間がかかりすぎる。
wlt_cnn_135_170906.png

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

白線追従走行用畳み込みニューラルネットワークのチューニング”では、Latency が 26924クロックだったが、今回は 20065 クロックになっている。Loop を見ると、CONV1_CONV2_CONV3 の Initiation achieved が 14 クロックから 3 クロックに改善されている。
リソース使用量はBRAM_18K が 4 個から 2 個に減った。DSP48E も 27 個から 24 個に減少している。FF は 9909 個から 10981 個に増えている。LUT も 15033 個から 15748 個に増えている。下に”白線追従走行用畳み込みニューラルネットワークのチューニング”の時のC コードの合成結果を貼っておく。
wlt_cnn_129_170904.png

次に、Export RTL を行った。結果を示す。
wlt_cnn_137_170906.png

遅延が 11.385 ns でやはり 100 MHz では動作しないようだ。これは約 87 MHz に相当する。
DSP が 4 個に減ってしまっている。これは大丈夫なのだろうか?
  1. 2017年09月06日 03:59 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で実装した畳み込みニューラルネットワークの指示子による性能差

Vivado HLS で実装した白線追従用の畳み込みニューラルネットワークで指示子を入れていないときと入れたときの性能差を比べてみよう。

Vivado HLS で実装した白線追従用の畳み込みニューラルネットワークで指示子を入れていないときの実装は、”白線追従走行用畳み込みニューラルネットワークの製作19(Cコードの合成、IP化)”で、指示子を入れたときの実装は”白線追従走行用畳み込みニューラルネットワークのチューニング”だ。

指示子を入れていない、”白線追従走行用畳み込みニューラルネットワークの製作19(Cコードの合成、IP化)”のLatency は187374 クロックだった。
wlt_cnn_88_170830.png

指示子を入れたときの実装の”白線追従走行用畳み込みニューラルネットワークのチューニング”では、26924 クロックだった。
wlt_cnn_130_170905.png

性能差は、187374 / 26924 ≒ 6.96 倍だった。

次に、Loop ごとの使用クロックを見てみよう。
指示子を入れていない、”白線追従走行用畳み込みニューラルネットワークの製作19(Cコードの合成、IP化)”のLoop ごとの使用クロックを示す。指示子を入れていないので、各Loop の使用クロックがそのまま表示されている。CONVとaf1_dot1 のループ数が多いのが分かる。
wlt_cnn_133_170905.png

指示子を入れたときの実装の”白線追従走行用畳み込みニューラルネットワークのチューニング”では、各ループがPIPELINE指示子によって、展開されてまとめられているのが分かる。使用クロック数も少なくなっている。
wlt_cnn_131_170905.png

最後にリソース使用量を見ていこう。
指示子を入れていない、”白線追従走行用畳み込みニューラルネットワークの製作19(Cコードの合成、IP化)”のリソース使用量を示す。
BRAM_18K は 13 個、DSP48E が 9 個、FF は716 個、LUT は 1476 個使用している。
wlt_cnn_134_170905.png

指示子を入れたときの実装の”白線追従走行用畳み込みニューラルネットワークのチューニング”のリソース使用量を示す。
BRAM_18K は 4 個、DSP48E が 27 個、FF は9909 個、LUT は 15033 個使用している。
wlt_cnn_132_170905.png

指示子を入れていない実装に対して、指示子を入れた実装は、BRAM_18K が約 0.31 倍、DSP48E が 3 倍、FF が約 13.8 倍、LUT が約 21.0 倍、LUT が約 10.2 倍になった。
BRAM_18K 以外は、指示子を入れていない実装のほうが指示子を入れた実装よりもリソース使用量が少なかった。

と言う訳で、性能とリソース使用量はトレードオフの関係にあるということができると思う。白線追従の用途では、指示子を入れない実装の処理速度で十分だ。
  1. 2017年09月05日 05:09 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

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

白線追従走行用畳み込みニューラルネットワークの製作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

画像をリサイズするためにDMA Read IPをVivado HLSで製作した2(dmar4resize_gray)

画像をリサイズするためにDMA Read IPをVivado HLSで製作した1(dmar4resize_gray)”の続き。

前回は、フレームバッファからDMA Read して AXI4 Stream 出力して resize_gray にAXI4 Stream 入力するIP (dmar4resize_gray)のC シミュレーションを行った。Ubuntu 16.04 上のVivado HLS 2016.4 はC シミュレーションがうまく行かなかったが、Windows 10 Pro 上のVivado HLS 2016.4 はC シミュレーションがうまく行った。
今回は、C コードの合成、C/RTL協調シミュレーション、Export RTL を行う。

C コードの合成を行った。まずは、Ubuntu 16.04 上のVivado HLS 2016.4 の結果から示す。
wlt_cnn_121_170901.png
wlt_cnn_122_170901.png

Latency は 480010 クロックで、これは、800 x 600 ピクセルの画像をDMAしているので、余計なクロックは 10 クロックのみである。
リソース使用量はBRAM_18K は 2 個、DSP48E が 0 個、FF が 845 個、LUT が 1047 個だった。

次に、Windows 10 Pro 上のVivado HLS 2016.4 のC コードの合成結果を示す。
wlt_cnn_123_170901.png

Ubuntu 16.04 上のVivado HLS 2016.4 の結果と同じだった。

Ubuntu 16.04 上のVivado HLS 2016.4 でC/RTL協調シミュレーションを行った。
wlt_cnn_124_170901.png

エラーになってしまった。エラー内容を示す。

ERROR: [COSIM 212-303] Aborting co-simulation: RTL simulation failed.
ERROR: [COSIM 212-344] Rtl simulation failed.
ERROR: [COSIM 212-4] *** C/RTL co-simulation finished: FAIL ***
could not read "C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO/test/dmar4resize_gray/solution1/sim/tv/rtldatafile/sim/report/cosim.log": no such file or directory
while executing
"source C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO/test/dmar4resize_gray/solution1/cosim.tcl"
invoked from within
"hls::main C:/Users/Masaaki/Documents/VIvado_HLS/ZYBO/test/dmar4resize_gray/solution1/cosim.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/RTL cosimulation.


Windows 10 Pro 上のVivado HLS 2016.4 でC/RTL協調シミュレーションを行った。
wlt_cnn_125_170901.png

やはり、同様のエラーだった。

C/RTL協調シミュレーションはあきらめて、Export RTL を行った。
Ubuntu 16.04 上のVivado HLS 2016.4
wlt_cnn_126_170901.png

SLICE 216 個、LUT 488 個、FF 752 個、DSP 0 個、BRAM 1 個、SRL 21 個使用していた。

Windows 10 Pro 上のVivado HLS 2016.4
wlt_cnn_127_170901.png

結果はUbuntu 16.04 上のVivado HLS 2016.4 と同じだった。
  1. 2017年09月03日 05:20 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

画像をリサイズするためにDMA Read IPをVivado HLSで製作した1(dmar4resize_gray)

Vivado HLS で画像のサイズを縮小して白黒変換(resize_gray)”でAXI4 Stream 入力の画像をリサイズして白黒変換するIP (resize_gray))を作成した。今回は、フレームバッファからDMA Read して AXI4 Stream 出力して resize_gray にAXI4 Stream 入力するIP (dmar4resize_gray)を作成する。

dmar4resize_gray は、ダブルバッファリングを行うDMAw4Gabor の RorL 信号を受けて、現在書き込んでいるフレームバッファを避け、もう片方のフレームバッファから画像データを取得するDMA Read IP となっている。

まずはVirtualBox 上のUbuntu 16.04 の Vivado HLS 2016.4 で作ってみた。
dmar4resize_gray.h と dmar4resize_gray.cpp それに、dmar4resize_gray_tb.cpp を作成した。そして、bmp_header.h と straight0.bmp と left_turn4.bmp を用意した。
wlt_cnn_111_170901.png

C シミュレーションを行った。エラーになってしまった。
wlt_cnn_112_170901.png

エラー内容を示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../dmar4resize_gray_tb.cpp in debug mode
csim.mk:73: ターゲット 'obj/dmar4resize_gray_tb.o' のレシピで失敗しました
../../../dmar4resize_gray_tb.cpp: In function ‘int main()’:
../../../dmar4resize_gray_tb.cpp:98:61: error: cast from ‘int*’ to ‘unsigned int’ loses precision [-fpermissive]
../../../dmar4resize_gray_tb.cpp:99:20: error: cast from ‘int*’ to ‘unsigned int’ loses precision [-fpermissive]
../../../dmar4resize_gray_tb.cpp:103:61: error: cast from ‘int*’ to ‘unsigned int’ loses precision [-fpermissive]
../../../dmar4resize_gray_tb.cpp:104:20: error: cast from ‘int*’ to ‘unsigned int’ loses precision [-fpermissive]
make: *** [obj/dmar4resize_gray_tb.o] エラー 1
CRITICAL WARNING: [SIM 100] 'csim_design' failed: compilation error(s).
INFO: [SIM 3] *************** CSIM finish ***************


どうやら 64 ビットアドレスなので、int * が unsigned int にキャストできない様だ。キャストすると 32 ビット分の情報が抜けてしまう。けど、Zynq は32ビットアドレスなので、64 ビットアドレスの必要が無い。エラーメッセージにある -fpermissive オプションを追加することにした。
やり方を説明する。
最初にProject メニューからProject Settings... を選択する。

Project Settings ダイアログが表示される。左のペインからSimulation をクリックし、TestBench Files で dmar4resize_gray_tb.cpp を選択する。
右のEdit CFLAGS... ボタンをクリックする。
wlt_cnn_113_170901.png

Edit CFLAGS Dialog で CFLAGS Value に -fpermissive を入力した。
wlt_cnn_114_170901.png

TestBench Files の dmar4resize_gray_tb.cpp の CFLAGS に -fpermissive オプションが追加された。
wlt_cnn_115_170901.png

もう一度、C シミュレーションを行った。
wlt_cnn_116_170901.png

C シミュレーションは一応成功しているが、ワーニングが出ている。ワーニングを示す。

WARNING: Hls::stream 'hls::stream >.1' contains leftover data, which may result in RTL simulation hanging.


AXI4 Stream のデータが無いといっているのかな?
~/Vivado_HLS/ZYBO/dmar4resize_gray/solution1/csim/build ディレクトリを見た。
wlt_cnn_117_170901.png

dma_result0.bmp と dma_result1.bmp が真っ暗なのが分かる。


これはおかしいので、Windows でもやってみた。Windows 10 Pro 上でVivado HLS 2016.4 を使用している。
wlt_cnn_118_170901.png

C シミュレーションを行ったが、エラーもワーニングも出ていない。
wlt_cnn_119_170901.png

C:\Users\Masaaki\Documents\VIvado_HLS\ZYBO\test\dmar4resize_gray\solution1\csim\build フォルダを見ると、dma_result0.bmp と dma_result1.bmp がきちんとコピーされていた。
wlt_cnn_120_170901.png

VirtualBox 上のUbuntu 16.04 の Vivado HLS 2016.4 ではC シミュレーションがうまく動かないが、Windows 10 Pro 上でVivado HLS 2016.4 では C シミュレーションがうまく行った。

ソースコードを貼っておく。
dmar4resize_gray.h を貼っておく。

// dmar4resize_gray.h
// 2017/09/01 by marsee
//

#ifndef __DMAR4RESIZE_GRAY_H__
#define __DMAR4RESIZE_GRAY_H__

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define MAX_FRAME_NUMBER    2

#define MEMCPY_LENGTH    (HORIZONTAL_PIXEL_WIDTH*4)

#endif


dmar4resize_gray.cpp を貼っておく。

// dmar4resize_gray.cpp
// 2017/09/01 by marsee
//
// if RorL=0 then frame_buffer1 read
// if RorL=1 then frame_buffer0 read
//

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

#include "dmar4resize_gray.h"

int dmar4resize_gray(volatile int *ins, hls::stream<ap_axis<32,1,1,1> >& outs,
        unsigned int frame_buffer0, unsigned int frame_buffer1,
        ap_uint<1> & RorL){
#pragma HLS INTERFACE ap_none port=RorL
#pragma HLS INTERFACE s_axilite port=frame_buffer0
#pragma HLS INTERFACE s_axilite port=frame_buffer1
#pragma HLS INTERFACE m_axi depth=5000000 port=ins offset=off
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    int dma_index;

    if(RorL == (ap_uint<1>)0// 1つ前のフレームバッファを読みだす
        dma_index = frame_buffer1/sizeof(int);
    else
        dma_index = frame_buffer0/sizeof(int);

    for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            pix.data = ins[dma_index+(y*HORIZONTAL_PIXEL_WIDTH)+x];

            if (y==0 && x==0)
                pix.user = 1;
            else
                pix.user = 0;

            if (x == (HORIZONTAL_PIXEL_WIDTH-1))
                pix.last = 1;
            else
                pix.last = 0;

            outs << pix;
        }
    }

    return 0;
}


dmar4resize_gray_tb.cpp を貼っておく。

// dmar4resize_gray_tb.cpp
// 2017/09/01 by marsee
//
// if RorL=0 then frame_buffer1 read
// if RorL=1 then frame_buffer0 read
//

#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 "dmar4resize_gray.h"
#include "bmp_header.h"

int dmar4resize_gray(volatile int *ins, hls::stream<ap_axis<32,1,1,1> >& outs,
        unsigned int frame_buffer0, unsigned int frame_buffer1,
        ap_uint<1> & RorL);

int main()
{
    using namespace std;

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

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpr2, *fbmpw;
    int *rd_bmp, *hw_lapd;
    int blue, green, red;
    ap_uint<2> active_frame = 0;
    ap_uint<1> RorL;
    char output_file[200];

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

    fread(&bmpfhr.bfType, sizeof(char), 2, fbmpr2);
    fread(&bmpfhr.bfSize, sizeof(long), 1, fbmpr2);
    fread(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpr2);
    fread(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpr2);
    fread(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpr2);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr2);

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

    int *buf;
    if ((buf =(int *)malloc(MAX_FRAME_NUMBER * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate buf 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);

    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr2);
            green = fgetc(fbmpr2);
            red = fgetc(fbmpr2);
            rd_bmp[bmpihr.biHeight*bmpihr.biWidth+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr2);

    RorL = 0;
    dmar4resize_gray((volatile int *)0, outs, (unsigned int)rd_bmp,
        (unsigned int)rd_bmp+(bmpihr.biWidth * bmpihr.biHeight) * sizeof(int),
        RorL);

    RorL = 1;
    dmar4resize_gray((volatile int *)0, outs, (unsigned int)rd_bmp,
        (unsigned int)rd_bmp+(bmpihr.biWidth * bmpihr.biHeight) * sizeof(int),
        RorL);

    // outs ストリームのデータを buf に入力する
    for (int k=0; k<MAX_FRAME_NUMBER; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(int i=0; i < bmpihr.biWidth; i++){
                outs >> vals;
                ap_int<32> val = vals.data;
                buf[(k*bmpihr.biWidth*bmpihr.biHeight)+(j*bmpihr.biWidth)+i] = (int)val;
            }
        }
    }

    // DMAされたデータをBMPフィルに書き込む
    for (int i=0; i<MAX_FRAME_NUMBER; i++){
        switch (i){
            case 0:
                strcpy(output_file,"dma_result0.bmp");
                break;
            case 1:
                strcpy(output_file,"dma_result1.bmp");
                break;
            case 2:
                strcpy(output_file,"dma_result2.bmp");
                break;
        }
        if ((fbmpw=fopen(output_file, "wb")) == NULL){
            fprintf(stderr, "Can't open %s by binary write mode\n", output_file);
            exit(1);
        }
        // BMPファイルヘッダの書き込み
        fwrite(&bmpfhr.bfType, sizeof(char), 2, fbmpw);
        fwrite(&bmpfhr.bfSize, sizeof(long), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpw);
        fwrite(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpw);
        fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);

        // RGB データの書き込み、逆順にする
        int offset = i * bmpihr.biWidth * bmpihr.biHeight;
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpw);
                fputc(green, fbmpw);
                fputc(red, fbmpw);
            }
        }
        fclose(fbmpw);
    }
    free(rd_bmp);
    return(0);
}

  1. 2017年09月02日 04:39 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で画像のサイズを縮小して白黒変換(resize_gray)

2017/09/07:修正 resize_gray.cpp と resize_gray_tb.cpp をBGR からRGB に変更した。詳しくは、”Vivado HLS で画像のサイズを縮小して白黒変換2(resize_gray)”を参照のこと)

白線追従走行用畳み込みニューラルネットワークの製作20(CNN IPの戦略を考える)”の図で”1/10 まで縮小して白黒変換”のブロックがある。
wlt_cnn_98_170831.png

その”1/10 まで縮小して白黒変換”ブロックをVivado HLS で作ることにする。ただし、図では”1/10 まで縮小して白黒変換”ブロックはAXI4 Master の予定だったが、性能が出せるようにDMA Read は分けようと思う。”1/10 まで縮小して白黒変換”ブロックはAXI4 Stream の入力ポートと出力ポートを持つように実装しよう。Vivado HLS の HLS ビデオライブラリの Resize() を使用して縮小することにしようと思う。

VirtualBox 上のUbuntu 16.04 のVivado HLS 2016.4 の resize_gray プロジェクトを示す。
wlt_cnn_100_170901.png

ソースコードは、resize_gray.h と resize_gray.cpp で、テストベンチは、resize_gray_tb.cpp と straight0.bmp だ。

C シミュレーションを行った。エラーは無かったが、二乗誤差が 1 の時は無視している。差が 1 の時は結構多い。
wlt_cnn_101_170901.png

~/Vivado_HLS/ZYBO/resize_gray/solution1/csim/build ディレクトリを示す。
straight0.bmp を元に 1/10 に縮小した test_straight0.bmp と test_straight0_cv.bmp だ。test_straight0.bmp がHLS ビデオライブラリを使用したハードウェア化コードで生成した画像で、test_straight0_cv.bmp がソフトウェアのOpenCV で生成した画像だ。
straight0.bmp を示す。
wlt_cnn_102_170901.png

test_straight0.bmp と test_straight0_cv.bmp を示す。(画像は拡大している)
wlt_cnn_103_170901.png

次に、C コードの合成を行った。結果を示す。
wlt_cnn_105_170901.png

wlt_cnn_106_170901.png

Latency の max は 507894 で、800 x 600 ピクセルの画像が 480000 なので、ほぼ1 クロックで 1 ピクセルの処理ができている。 507894 / 480000 ≒ 1.058
リソース使用量は DSP48E は 19 個でやはりかなり使用している。

C/RTL協調シミュレーションを行った。
wlt_cnn_107_170901.png

こちらもLatency は 501405 でほぼ1 クロックで 1 ピクセルの処理ができている。

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

outs のTVALID がまばらに立っていることが分かる。これは、画像を 1/10 に縮小しているからだ。
outs のTVALID が立っているところの部分を拡大してみよう。
wlt_cnn_110_170901.png

IP化のために、Export RTL を行った。なお、Vivado RTL Synthesis と Place and Route にチェックを入れてある。
wlt_cnn_109_170901.png

やはり、DSP は 19 個使用している。

resize_gray.h を貼っておく。

// resize_gray.h
// 2017/08/31 by marsee
//

#ifndef __resize_gray_H__
#define __resize_gray_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT    600
#define MAX_WIDTH    800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;
#endif


resize_gray.cpp を貼っておく。

// resize_gray.cpp
// 2017/08/31 by marsee
//

#include "resize_gray.h"

int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs){
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS DATAFLOW
#pragma HLS INTERFACE s_axilite port=return

    RGB_IMAGE org_img(600800);
    GRAY_IMAGE org_img_g(600800);
    GRAY_IMAGE resize_img_g(4560);
    RGB_IMAGE resize_img(4560);

    hls::AXIvideo2Mat(ins, org_img);
    hls::CvtColor<HLS_BGR2GRAY>(org_img, org_img_g);
    hls::Resize(org_img_g, resize_img_g);
    hls::CvtColor<HLS_GRAY2BGR>(resize_img_g, resize_img);
    hls::Mat2AXIvideo(resize_img, outs);

    return(0);
}


resize_gray_tb.cpp を貼っておく。

// resize_gray_tb.cpp
// 2017/08/31 by marsee
//

#include <iostream>
#include "hls_opencv.h"
#include "resize_gray.h"

using namespace cv;

#define INPUT_IMAGE        "straight0.bmp"
#define OUTPUT_IMAGE    "test_straight0.bmp"
#define OUTPUT_IMAGE_CV    "test_straight0_cv.bmp"

void resize_gray(AXI_STREAM& ins, AXI_STREAM& outs);
void opencv_resize_gray(Mat& src, Mat& dst);

int main (int argc, char** argv) {
    // OpenCV で 画像を読み込む
    Mat src = imread(INPUT_IMAGE);
    AXI_STREAM src_axi, dst_axi;

    // Mat フォーマットから AXI4 Stream へ変換
    cvMat2AXIvideo(src, src_axi);

    // resize_gray() 関数をコール
    resize_gray(src_axi, dst_axi);

    // AXI4 Stream から Mat フォーマットへ変換
    // dst は宣言時にサイズとカラー・フォーマットを定義する必要がある
    Mat dst(4560, CV_8UC3);
    AXIvideo2cvMat(dst_axi, dst);

    // Mat フォーマットからファイルに書き込み
    imwrite(OUTPUT_IMAGE, dst);

    // opencv_resize_gray() をコール
    Mat dst_cv(4560, CV_8UC3);
    opencv_resize_gray(src, dst_cv);
    imwrite(OUTPUT_IMAGE_CV, dst_cv);

    // dst と dst_cv が同じ画像かどうか?比較する
    for (int y=0; y<45; y++){
        Vec3b* dst_ptr = dst.ptr<Vec3b>(y);
        Vec3b* dst_cv_ptr = dst_cv.ptr<Vec3b>(y);
        for (int x=0; x<60; x++){
            Vec3b dst_bgr = dst_ptr[x];
            Vec3b dst_cv_bgr = dst_cv_ptr[x];

            // bgr のどれかが間違っていたらエラー
            if (std::pow(dst_bgr[0]-dst_cv_bgr[0], 2.0) > 1 || std::pow(dst_bgr[1]-dst_cv_bgr[1], 2.0) > 1
                    || std::pow(dst_bgr[2]-dst_cv_bgr[2], 2.0) > 1){
                printf("x = %d, y = %d,  Error dst=%d,%d,%d dst_cv=%d,%d,%d\n", x, y,
                        dst_bgr[0], dst_bgr[1], dst_bgr[0], dst_cv_bgr[0], dst_cv_bgr[1], dst_cv_bgr[2]);
                //return 1;
            }
        }
    }
    printf("Test with 0 errors.\n");

    return 0;
}

void opencv_resize_gray(Mat& src, Mat& dst){
    Mat gray(src.rows, src.cols, CV_8UC1);
    Mat img0g(4560, CV_8UC1);

    cvtColor(src, gray, CV_BGR2GRAY);
    resize(gray, img0g, img0g.size(), 00, INTER_LINEAR);
    cvtColor(img0g, dst, CV_GRAY2BGR);
}

  1. 2017年09月01日 05:11 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0