how to code something このページをアンテナに追加 RSSフィード

2017-12-23

Zybo+OpenCVでリアルタイム画像処理

以下の記事を試す
32. Linux on Zynq / Real-Time Video Processing
https://sites.google.com/site/powellsshowcase/home/32linuxonzynqreal-timevideoprocessing

1. GitHubレポジトリクローン

git clone https://github.com/andrewandrepowell/zybo_petalinux_video_hls

2. ビットストリームを生成

Vivadoを起動し、zybo_petalinux_video_hls.xprを開く
Automatically Upgrade IPを指定して、IPコアをアップグレードする。
PSのGPIOを使う必要があるので、ZYNQでMIO GPIOの設定を行う。
ZYNQブロックをダブルクリックし、MIO Configurationを開く
GPIO MIO=MIO、EMIO GPIO(Width)=64に設定した。
その後Generate Bitstreamする。
f:id:seinzumtode:20171228094306p:image

3. クロスコンパイル環境(Debian)にOpenCVインストールする

OpenCVインストールはこの記事を参考:
Zynq上のDebianにpyenvでPython3とOpencvインストール
http://d.hatena.ne.jp/seinzumtode/20171123/1511470206
Pythonのバージョンは3.4.7(pyenv), OpenCVのバージョンは3.0.0
(クロスコンパイルDebian環境の作り方は以下の記事を参照:)
ubuntuにzynqのクロス環境debianをdebootstrapで入れる
http://d.hatena.ne.jp/seinzumtode/20171225/1514177692

4. SDKの起動

まず古いハードウェアプロジェクトを削除する(VivadoからLaunch SDKして、block_design_0_wrapperプロジェクトをDeleteする)
その後VivadoでハードウェアをExportし、Launch SDKSDKを起動する。
今回試したいLinuxプロジェクトはvideo_processing_app_0である。
main.ccを開くと、OpenCV関連のヘッダファイルが見えていないので、以降でこれらを追加する

5. Linuxプロジェクトの設定

video_processing_app_0アプリケーションプロジェクトを右クリックして、C/C++ Build Settingsを開く
f:id:seinzumtode:20171228100025p:image

6. コンパイラの設定

f:id:seinzumtode:20171228100409p:image
f:id:seinzumtode:20171228101306p:image

7. リンカースクリプトの設定

f:id:seinzumtode:20171228101644p:image
リンクするライブラリ(-l)
デフォルトではlibtiffとかlibjpegとか"lib"の接頭辞がついていたので、それらは外す)

dl
opencv_videoio
opencv_imgcodecs
opencv_highgui
opencv_imgproc
opencv_photo
opencv_video
opencv_features2d
opencv_objdetect
opencv_shape
opencv_core
opencv_ml
tiff
webp
IlmImf
png
jpeg
jasper
z
pthread
rt

リンカーフラグでsysrootとrpathを指定する。
20180321追記:rpathの指定はrpath=dirのように、=(イコール)が必要。-Wl,-rpath=のところで、-Wl, -rpathのように空白をいれない。

--sysroot="/home/shohei/zynqfs" -Wl,-rpath=/home/shohei/zynqfs/lib/arm-linux-gnueabihf -Wl,-rpath=/home/shohei/zynqfs/usr/lib/arm-linux-gnueabihf

f:id:seinzumtode:20171228101842p:image
これでプログラムビルドできた。

8. Petalinuxの設定

いつも通りにビルドする。
カーネル設定で必要なのはUVCカメラ(V4L)、Userspace I/Oあたり。
生成したbitstreamを元に、BOOT.BINを用意する

9. デバイスツリーの設定

ここで激しくハマった。FilterのVDMAにはReadとWriteの2チャンネルあるのだが、それぞれに対してトップモジュール(というのか?)的にデバイス宣言しないといけない。(下でいう、dma0とdma1)
UVCカメラの設定をしておくのと(次を参考→Zybo+PetalinuxでUVCカメラを使う:http://d.hatena.ne.jp/seinzumtode/20171120/1511167136 )、各デバイスはuioとして見えるようにしておく(compatible = generic=uio)。
DTSの設定はここを参考に:(https://github.com/andrewandrepowell/zybo_petalinux_video_hls/tree/master/petalinux_proj/subsystems/linux/configs/device-tree
以下の設定をすることで、Linuxに入ったときに、uioが見えるようになる。

shohei@debian8-zynq:~$ ls -l /dev/uio*
crw------- 1 root root 245, 0  1月  1 00:00 /dev/uio0
crw------- 1 root root 245, 1  1月  1 00:00 /dev/uio1
crw------- 1 root root 245, 2  1月  1 00:00 /dev/uio2
crw------- 1 root root 245, 3  1月  1 00:00 /dev/uio3
crw------- 1 root root 245, 4  1月  1 00:00 /dev/uio4
crw------- 1 root root 245, 5  1月  1 00:00 /dev/uio5
crw------- 1 root root 245, 6  1月  1 00:00 /dev/uio6

shohei@debian8-zynq:~$ cat /sys/class/uio/uio*/maps/map0/name
/amba_pl/axi_dispctrl@43c00000
/amba_pl/gpio@41200000
/amba_pl/dma@43000000
/amba_pl/dma0@43010000
/amba_pl/dma1@43010000
/amba_pl/filt@43c10000
/amba_pl/gpio@41210000

system.dtsの抜粋
PLの設定

	amba_pl {
		#address-cells = <0x1>;
		#size-cells = <0x1>;
		compatible = "simple-bus";
		ranges;

		axi_dispctrl@43c00000 {
			compatible = "generic-uio";
			reg = <0x43c00000 0x10000>;
			xlnx,blue-width = <0x5>;
			xlnx,green-width = <0x6>;
			xlnx,red-width = <0x5>;
			xlnx,use-bufr-div5 = <0x0>;
		};

		gpio@41200000 {
			#gpio-cells = <0x2>;
			#interrupt-cells = <0x2>;
			compatible = "generic-uio";
			gpio-controller;
			interrupt-controller;
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x1d 0x4>;
			reg = <0x41200000 0x10000>;
			xlnx,all-inputs = <0x1>;
			xlnx,all-inputs-2 = <0x0>;
			xlnx,all-outputs = <0x0>;
			xlnx,all-outputs-2 = <0x1>;
			xlnx,dout-default = <0x0>;
			xlnx,dout-default-2 = <0x0>;
			xlnx,gpio-width = <0x4>;
			xlnx,gpio2-width = <0x4>;
			xlnx,interrupt-present = <0x1>;
			xlnx,is-dual = <0x1>;
			xlnx,tri-default = <0xffffffff>;
			xlnx,tri-default-2 = <0xffffffff>;
		};

		dma@43000000 {
			#dma-cells = <0x1>;
			clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_mm2s_aclk";
			clocks = <0x1 0xf 0x1 0xf 0x1 0xf>;
			compatible = "generic-uio";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x1e 0x4>;
			reg = <0x43000000 0x10000>;
			xlnx,addrwidth = <0x20>;
			xlnx,flush-fsync = <0x1>;
			xlnx,num-fstores = <0x4>;

			dma-channel@43000000 {
				compatible = "xlnx,axi-vdma-mm2s-channel";
				interrupts = <0x0 0x1e 0x4>;
				xlnx,datawidth = <0x20>;
				xlnx,device-id = <0x0>;
				xlnx,genlock-mode;
			};
		};

		dma0@43010000 {
			#dma-cells = <0x1>;
			clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk", "m_axi_s2mm_aclk";
			clocks = <0x1 0xf 0x1 0xf 0x1 0xf 0x1 0xf 0x1 0xf>;
			compatible = "generic-uio";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x20 0x4>;
			reg = <0x43010000 0x10000>;
			xlnx,addrwidth = <0x20>;
			xlnx,flush-fsync = <0x1>;
			xlnx,num-fstores = <0x4>;

			dma-channel@43010000 {
				compatible = "xlnx,axi-vdma-mm2s-channel";
				interrupts = <0x0 0x20 0x4>;
				xlnx,datawidth = <0x20>;
				xlnx,device-id = <0x1>;
			};
		};

		dma1@43010000 {
			#dma-cells = <0x1>;
			clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk", "m_axi_s2mm_aclk";
			clocks = <0x1 0xf 0x1 0xf 0x1 0xf 0x1 0xf 0x1 0xf>;
			compatible = "generic-uio";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x21 0x4>;
			reg = <0x43010000 0x10000>;
			xlnx,addrwidth = <0x20>;
			xlnx,flush-fsync = <0x1>;
			xlnx,num-fstores = <0x4>;

			dma-channel@43010030 {
				compatible = "xlnx,axi-vdma-s2mm-channel";
				interrupts = <0x0 0x21 0x4>;
				xlnx,datawidth = <0x20>;
				xlnx,device-id = <0x1>;
				xlnx,genlock-mode;
			};
		};

		filt@43c10000 {
			compatible = "generic-uio";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x1f 0x4>;
			reg = <0x43c10000 0x10000>;
			xlnx,s-axi-control-bus-addr-width = <0x4>;
			xlnx,s-axi-control-bus-data-width = <0x20>;
		};

		gpio@41210000 {
			#gpio-cells = <0x2>;
			compatible = "generic-uio";
			gpio-controller;
			reg = <0x41210000 0x10000>;
			xlnx,all-inputs = <0x0>;
			xlnx,all-inputs-2 = <0x0>;
			xlnx,all-outputs = <0x1>;
			xlnx,all-outputs-2 = <0x0>;
			xlnx,dout-default = <0x1>;
			xlnx,dout-default-2 = <0x0>;
			xlnx,gpio-width = <0x1>;
			xlnx,gpio2-width = <0x20>;
			xlnx,interrupt-present = <0x0>;
			xlnx,is-dual = <0x0>;
			xlnx,tri-default = <0xffffffff>;
			xlnx,tri-default-2 = <0xffffffff>;
		};
	};

bootargsの設定(uioを有効化するための設定)

	chosen {
		bootargs = "console=ttyPS0,115200 earlyprintk root=/dev/nfs nfsroot=192.168.100.205:/tftpboot/nfsroot,tcp ip=dhcp rw uio_pdrv_genirq.of_id=generic-uio";
		stdout-path = "serial0:115200n8";
	};

Webカメラの設定

usb@e0002000 {
            compatible = "xlnx,zynq-usb-2.20a", "chipidea,usb2";
            status = "okay";
            clocks = <0x1 0x1c>;
            dr_mode = "host";  // ←←←←ここを追加
            interrupt-parent = <0x4>;
            interrupts = <0x0 0x15 0x4>;
            reg = <0xe0002000 0x1000>;
            usb-phy = <&usb_phy0>;   // ←←←←ここを追加
        };


/{ 
   usb_phy0: phy0 {
	compatible = "ulpi-phy";
	#phy-cells = <0>;
	reg = <0xe0002000 0x1000>;
	view-port = <0x170>;
	drv-vbus;
	};
};

9. Linuxを起動する。

OpenCVライブラリの場所を読み込むための引数としてLD_LIBRARY_PATHをつけて、さらにsudo権限で起動する。
起動スクリプトにした。
./run.sh

#!/bin/bash
sudo LD_LIBRARY_PATH=/home/shohei/.pyenv/versions/3.4.7/usr/local/lib/ ./video_process_app_0.elf

実行

shohei@debian8-zynq:~$ ./run.sh
[sudo] password for shohei:

Checking to see if the video stream opened and setting resolution...
Configuring display...
Configuring framebuffer with opencv Mats...
Configuring standalone GPIO driver...
Configuring filt core...
Running main application...

なんか下が切れた画像が表示されて止まる。
f:id:seinzumtode:20171228103843p:image
VDMAのWriteでページフレームを指定するSOF(Start of Frame)(AXIのtuser信号に乗っかってる?)が帰ってこないので無限にスタックしているようである。
(Vivado上でBlock diagramをみると、VDMA1に対してSOF on tuserを送出する必要があるのは、解像度変換を行っているHLS IPコアのようにみえる)
f:id:seinzumtode:20171228105513p:image
ソフトウェア上では、linuxmmap.cppのwait()の中でread(2)しているのだが、ここで無限読み込み待ち状態に入っている。
main.cc

                //ループ処理の最後
		/* Trigger VDMA with fsync. Wait until interrupt is triggered. */
		XGpio_DiscreteWrite( &fsync_obj, 1, 0 );
                //ここでVDMAのWrite(vdma_1_1=linuxmmapオブジェクト)に対してwait()する
		filt_vdma_mmap_1_obj.wait();
		filtvdma_clear_vdma_write_int( &filtvdma_obj );
		filt_vdma_mmap_1_obj.ready();
		XGpio_DiscreteWrite( &fsync_obj, 1, 1 );

linuxmmap.cpp

uint32_t linuxmmap::wait()
{
	if ( isuio )
	{
		uint32_t nints;
                //ここのread()で無限待機状態に入っている 
		if ( read( fd, &nints, sizeof( nints ) ) != sizeof( nints ) )
			throw std::runtime_error( "Interrupt failed!" );
		return nints;
	}
	return 0;
}

これ以上追えなくなってスタックしたので、PYNQのJupyter notebookからHDMI入力を入れて使う方法を進めてみることにする。

参考:

https://sites.google.com/site/powellsshowcase/home/32linuxonzynqreal-timevideoprocessing
https://www.xilinx.com/support/answers/69159.html
https://forums.xilinx.com/t5/Embedded-Linux/Petalinux-2017-1-Cross-Compiling/td-p/768533
https://forums.xilinx.com/t5/SDSoC-Environment-and-reVISION/How-to-link-OpenCV-library-with-SDSoC/td-p/761682
https://stackoverflow.com/questions/33519085/cannot-specify-multiple-rpath-in-eclipse-cdt
https://forum.digilentinc.com/topic/2719-how-to-register-my-device-as-uio-on-petalinux/
https://github.com/andrewandrepowell/zybo_petalinux_video_hls/tree/master/petalinux_proj/subsystems/linux/configs/device-tree