catch-img

Raspberry Pi Camera v2 on KV260(後編)

Raspberry Pi Camera v2 on KV260の後編です。

本ブログで紹介させていただく内容について、できる限り正確な情報提供を心がけておりますが、正確性や安全性を保証するものではありません。
また、リンク先の他サイトで提供される情報・サービスについても、責任を負いかねますのでご了承ください。

目次[非表示]

  1. 1.bitstreamファイルをbinファイルに変換する
  2. 2.devicetreeの生成
  3. 3.Xilinx Ubuntuを使って動かしてみる
    1. 3.1.yavtaのインストール
    2. 3.2.ファイルのコピー
    3. 3.3.devicetreeのロード
    4. 3.4.カメラ画像を表示する
  4. 4.おわりに
  5. 5.関連リンク
    1. 5.1.Raspberry Pi Camera v2 on KV260(前編)
    2. 5.2.Ubuntuイメージ(Xilinx公式)
    3. 5.3.NNgenの紹介
    4. 5.4.Raspberry Pi Camera-v2 on KV260
    5. 5.5.免責事項、著作権、商標権

bitstreamファイルをbinファイルに変換する

ここからはVitisをインストールしたUbuntu環境上でのコマンドライン操作になります。 Vivadoで生成したFPGAの回路情報であるbitstreamファイルをKV260で立ち上げるLinux環境で扱えるローダブルイメージに変換します。 ここでは pl.bif というローダブルイメージ作成用の設定ファイルを作成し、 bootgen というVitisをインストールすると同時にインストールされているスクリプトを使い、bitファイルをbinファイルに変換します。

yamano@Awesome_FEK:~/kv260_rpi/vivado$ source /opt/Xilinx/Vitis/2022.1/settings64.sh
yamano@Awesome_FEK:~/kv260_rpi/vivado$ cat << EOF >> pl.bif
all:
{
    [destination_device = pl] kv260_mipicsi2_simpleISP.bit
}
EOF
yamano@Awesome_FEK:~/kv260_rpi/vivado$ bootgen -arch zynqmp -image pl.bif \
                                           -o kv260_mipicsi2_simpleISP.bin

変換が完了すると kv260_mipicsi2_simpleISP.bin という名前でbinファイルが生成されます。

devicetreeの生成

次に先ほどVivadoからエクスポートしたxsaファイルを用いてdevicetreeを生成します。 この章で生成するdevicetreeファイルも Raspberry Pi Camera-v2 on KV260 に上げているのでご利用ください。 以下は生成方法の紹介になります。

yamano@Awesome_FEK:~/kv260_rpi$ git clone -b xilinx_v2022.1 https://github.com/Xilinx/device-tree-xlnx.git
yamano@Awesome_FEK:~/kv260_rpi$ cd vivado 
yamano@Awesome_FEK:~/kv260_rpi/vivado$ xsct
xsct% hsi::open_hw_design kv260_mipicsi2_simpleISP/design_1_wrapper.xsa
xsct% hsi::set_repo_path ../device-tree-xlnx 
xsct% hsi::create_sw_design device-tree -os device_tree -proc psu_cortexa53_0 
xsct% hsi::set_property CONFIG.dt_overlay true [hsi::get_os] 
xsct% hsi::generate_target -dir rpi_cam_overlay 
xsct% exit

ここまでで rpi_cam_overlay というディレクトリにdevicetreeファイルが生成されます。 以降で利用するのは pl.dtsi というファイルで保存されているdevicetreeファイルです。 生成されるdevicetreeはそのままではISPを正しく設定できないものになっています。 なので修正した部分との差分を載せておきます。

--- org.dtsi    2022-07-28 16:47:16.135044103 +0900
+++ pl.dtsi     2022-07-28 19:45:04.411100055 +0900
@@ -13,7 +13,7 @@
                overlay0: __overlay__ {
                        #address-cells = <2>;
                        #size-cells = <2>;
-                       firmware-name = "design_1_wrapper.bit.bin";
+                       firmware-name = "kv260_mipicsi2_simpleISP.bin";
                        resets = <&zynqmp_reset 116>;
                };
        };
@@ -43,8 +43,36 @@
                };
        };
        fragment@2 {
-               target = <&amba>;
+               target-path="/";
                overlay2: __overlay__ {
+                       imx219_vana: fixedregulator0 {
+                               compatible = "regulator-fixed";
+                               regulator-name = "imx219_vana";
+                               regulator-min-microvolt = <2800000>;
+                               regulator-max-microvolt = <2800000>;
+                       };
+                       imx219_vdig: fixedregulator1 {
+                               compatible = "regulator-fixed";
+                               regulator-name = "imx219_vdig";
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                       };
+                       imx219_vddl: fixedregulator2 {
+                               compatible = "regulator-fixed";
+                               regulator-name = "imx219_vddl";
+                               regulator-min-microvolt = <1200000>;
+                               regulator-max-microvolt = <1200000>;
+                       };
+                       imx219_clk:camera-clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <24000000>;
+                       };
+               };
+       };
+       fragment@3 {
+               target = <&amba>;
+               overlay3: __overlay__ {
                        #address-cells = <2>;
                        #size-cells = <2>;
                        axi_iic_0: i2c@80030000 {
@@ -57,6 +85,40 @@
                                interrupt-parent = <&gic>;
                                interrupts = <0 104 4>;
                                reg = <0x0 0x80030000 0x0 0x10000>;
+                               iic_mux_0: i2c_mux@74 { /* u18 */
+                                       compatible = "nxp,pca9546";
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       reg = <0x74>;
+                                       isa0_i2c0: i2c@0 {
+                                               reg = <0>;
+                                       };
+                                       isa1_i2c1: i2c@1 {
+                                               reg = <1>;
+                                       };
+                                       rpi_i2c2: i2c@2 {
+                                               #address-cells = <1>;
+                                               #size-cells = <0>;
+                                               reg = <2>;
+                                               imx219: sensor@10 {
+                                                       compatible = "sony,imx219";
+                                                       reg = <0x10>;
+                                                       clocks = <&imx219_clk>;
+                                                       VANA-supply = <&imx219_vana>;   /* 2.8v */
+                                                       VDIG-supply = <&imx219_vdig>;   /* 1.8v */
+                                                       VDDL-supply = <&imx219_vddl>;   /* 1.2v */
+                                                       port {
+                                                               imx219_0: endpoint {
+                                                                       remote-endpoint = <&mipi_csi_inmipi_csi2_rx_subsyst_0>;
+                                                                       data-lanes = <1 2>;
+                                                                       clock-noncontinuous;
+                                                                       link-frequencies = /bits/ 64 <456000000>;
+                                                               };
+                                                       };
+                                               };
+                                       };
+                               /* Bus 4/4 are not connected */
+                               };
                        };
                        misc_clk_0: misc_clk_0 {
                                #clock-cells = <0>;
@@ -66,13 +128,16 @@
                        mipi_csi2_rx_subsyst_0: mipi_csi2_rx_subsystem@80002000 {
                                clock-names = "lite_aclk", "dphy_clk_200M", "video_aclk";
                                clocks = <&misc_clk_0>, <&misc_clk_1>, <&misc_clk_1>;
-                               compatible = "xlnx,mipi-csi2-rx-subsystem-5.1", "xlnx,mipi-csi2-rx-subsystem-5.0";
+                               compatible = "xlnx,mipi-csi2-rx-subsystem-5.0";
                                interrupt-names = "csirxss_csi_irq";
                                interrupt-parent = <&gic>;
                                interrupts = <0 105 4>;
                                reg = <0x0 0x80002000 0x0 0x2000>;
                                xlnx,axis-tdata-width = <32>;
                                xlnx,csi-pxl-format = <0x2b>;
+                               xlnx,en-active-lanes;
+                               xlnx,en-csi-v2-0;
+                               xlnx,en-vcx;
                                xlnx,max-lanes = <2>;
                                xlnx,ppc = <2>;
                                xlnx,vc = <4>;
@@ -83,9 +148,9 @@
                                        mipi_csi_port1mipi_csi2_rx_subsyst_0: port@1 {
                                                /* Fill cfa-pattern=rggb for raw data types, other fields video-format and video-width user needs to fill */
                                                reg = <1>;
-                                               xlnx,cfa-pattern = "rggb";
-                                               xlnx,video-format = <12>;
-                                               xlnx,video-width = <8>;
+                                               //xlnx,cfa-pattern = "rggb";
+                                               //xlnx,video-format = <12>;
+                                               xlnx,video-width = <10>;
                                                mipi_csirx_outmipi_csi2_rx_subsyst_0: endpoint {
                                                        remote-endpoint = <&v_demosaic_0mipi_csi2_rx_subsyst_0>;
                                                };
@@ -94,10 +159,11 @@
                                                /* Fill cfa-pattern=rggb for raw data types, other fields video-format,video-width user needs to fill */
                                                /* User need to add something like remote-endpoint=<&out> under the node csiss_in:endpoint */
                                                reg = <0>;
-                                               xlnx,cfa-pattern = "rggb";
-                                               xlnx,video-format = <12>;
-                                               xlnx,video-width = <8>;
+                                               //xlnx,cfa-pattern = "rggb";
+                                               //xlnx,video-format = <12>;
+                                               xlnx,video-width = <10>;
                                                mipi_csi_inmipi_csi2_rx_subsyst_0: endpoint {
+                            remote-endpoint = <&imx219_0>;
                                                        data-lanes = <1 2>;
                                                };
                                        };
@@ -127,7 +193,8 @@
                                        demosaic_port1v_demosaic_0: port@1 {
                                                /* For cfa-pattern=rggb user needs to fill as per BAYER format */
                                                reg = <1>;
-                                               xlnx,cfa-pattern = "rggb";
+                                               //xlnx,cfa-pattern = "rggb";
+                                               //xlnx,video-format = <2>;
                                                xlnx,video-width = <10>;
                                                demo_outv_demosaic_0: endpoint {
                                                        remote-endpoint = <&v_gamma_lut_0v_demosaic_0>;
@@ -136,7 +203,7 @@
                                        demosaic_port0v_demosaic_0: port@0 {
                                                /* For cfa-pattern=rggb user needs to fill as per BAYER format */
                                                reg = <0>;
-                                               xlnx,cfa-pattern = "rggb";
+                                               //xlnx,cfa-pattern = "rggb";
                                                xlnx,video-width = <10>;
                                                v_demosaic_0mipi_csi2_rx_subsyst_0: endpoint {
                                                        remote-endpoint = <&mipi_csirx_outmipi_csi2_rx_subsyst_0>;
@@ -182,6 +249,7 @@
                                        #size-cells = <0>;
                                        gamma_port1v_gamma_lut_0: port@1 {
                                                reg = <1>;
+                                               xlnx,video-format = <2>;
                                                xlnx,video-width = <10>;
                                                gamma_outv_gamma_lut_0: endpoint {
                                                        remote-endpoint = <&v_proc_ss_cscv_gamma_lut_0>;
@@ -189,6 +257,7 @@
                                        };
                                        gamma_port0v_gamma_lut_0: port@0 {
                                                reg = <0>;
+                                               xlnx,video-format = <2>;
                                                xlnx,video-width = <10>;
                                                v_gamma_lut_0v_demosaic_0: endpoint {
                                                        remote-endpoint = <&demo_outv_demosaic_0>;
@@ -196,19 +265,19 @@
                                        };
                                };
                        };
-                       v_proc_ss_csc: v_proc_ss@a0040000 {
+                       v_proc_ss_csc: v_proc_ss_csc@a0040000 {
                                clock-names = "aclk";
                                clocks = <&misc_clk_1>;
-                               compatible = "xlnx,v-proc-ss-2.3", "xlnx,vpss-csc", "xlnx,v-vpss-csc";
+                               compatible = "xlnx,v-vpss-csc";
                                reg = <0x0 0xa0040000 0x0 0x10000>;
                                reset-gpios = <&gpio 80 1>;
-                               xlnx,colorspace-support = <0>;
-                               xlnx,csc-enable-window = "true";
+                               xlnx,colorspace-support = <1>;
+                               //xlnx,csc-enable-window = "true";
                                xlnx,max-height = <2160>;
                                xlnx,max-width = <3840>;
-                               xlnx,num-video-components = <3>;
+                               //xlnx,num-video-components = <3>;
                                xlnx,samples-per-clk = <2>;
-                               xlnx,topology = <3>;
+                               //xlnx,topology = <3>;
                                xlnx,use-uram = <0>;
                                xlnx,video-width = <10>;
                                csc_portsv_proc_ss_csc: ports {
@@ -218,7 +287,7 @@
                                                /* For xlnx,video-format user needs to fill as per their requirement */
                                                reg = <1>;
                                                xlnx,video-format = <3>;
-                                               xlnx,video-width = <10>;
+                                               xlnx,video-width = <8>;
                                                csc_outv_proc_ss_csc: endpoint {
                                                        remote-endpoint = <&v_proc_ss_scalerv_proc_ss_csc>;
                                                };
@@ -226,21 +295,21 @@
                                        csc_port0v_proc_ss_csc: port@0 {
                                                /* For xlnx,video-format user needs to fill as per their requirement */
                                                reg = <0>;
-                                               xlnx,video-format = <3>;
-                                               xlnx,video-width = <10>;
+                                               xlnx,video-format = <2>;
+                                               xlnx,video-width = <8>;
                                                v_proc_ss_cscv_gamma_lut_0: endpoint {
                                                        remote-endpoint = <&gamma_outv_gamma_lut_0>;
                                                };
                                        };
                                };
                        };
-                       v_proc_ss_scaler: v_proc_ss@a0080000 {
+                       v_proc_ss_scaler: v_proc_ss_scaler@a0080000 {
                                clock-names = "aclk_axis", "aclk_ctrl";
                                clocks = <&misc_clk_1>, <&misc_clk_1>;
-                               compatible = "xlnx,v-proc-ss-2.3", "xlnx,vpss-scaler-2.2", "xlnx,v-vpss-scaler-2.2", "xlnx,vpss-scaler";
+                               compatible = "xlnx,v-vpss-scaler-2.2";
                                reg = <0x0 0xa0080000 0x0 0x40000>;
                                reset-gpios = <&gpio 81 1>;
                                xlnx,colorspace-support = <0>;
                                xlnx,csc-enable-window = "true";
                                xlnx,enable-csc = "false";
                                xlnx,h-scaler-phases = <64>;
@@ -288,9 +357,9 @@
                                vcap_portsv_proc_ss_scaler: ports {
                                        #address-cells = <1>;
                                        #size-cells = <0>;
-                                       vcap_portv_proc_ss_scaler: port@0 {
-                                               direction = "input";
+                                       port@0 {
                                                reg = <0>;
+                                               direction = "input";
                                                v_frmbuf_wr_0v_proc_ss_scaler: endpoint {
                                                        remote-endpoint = <&sca_outv_proc_ss_scaler>;
                                                };

pl.dtsi はテキストファイルですが、Linuxに読み込ませるためにはバイナリファイルにコンパイルする必要があります。次のコマンドでデバイスツリーをバイナリファイルにコンパイルします。

yamano@Awesome_FEK:~/kv260_rpi/vivado/rpi_cam_overlay$ dtc -I dts -O dtb \
                                        -o kv260_mipicsi2_simpleISP.dtbo pl.dtsi 


以降にここで生成される kv260_mipicsi2_simpleISP.dtbo を利用します。

Xilinx Ubuntuを使って動かしてみる

いよいよKV260を立ち上げていきます。
OSは Xilinx公式のUbuntuイメージ が公開されているのでこれを利用します。

Xilinx公式のGetting Started に従い、Kria KV260 Kit用のUbuntuを起動できるSDカードを作成してください。

K26 SOM内部のファームウェアが工場出荷時のバージョンによりUbuntuが立ち上がらないケースがあるようです。 この場合 Xilinx Wikiの手順 に従いファームウェアをアップデートすることにより解消できる可能性があるようです。 ですが、ファームウェアのアップデートは最悪起動できなくなる可能性がございますので、 自己責任の上ご実施頂ければ幸いです。

  Kria K26 SOM - Xilinx Wiki - Confluence https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/1641152513/Kria+K26+SOM#Boot-Firmware-Updates


インターネットへの接続が必要なのでLANケーブルでブロードバンドルーターを接続しておきましょう。 では作成したSDカードを使ってKV260を起動します。 初回起動は少し時間がかかるようですが、10~15分ほどで立ち上がるので、 パスワードなどの初期設定を行いログインできるようになると、 次のようなデスクトップ画面が立ち上がります。

yavtaのインストール

次にISPの設定に必要となるyavtaというソフトウェアをインストールします。 ターミナルを開き以下のコマンドを打っていきます。

ubuntu@ubuntu:~$ sudo apt update
ubuntu@ubuntu:~$ sudo apt install -y yavta

ファイルのコピー

bitstreamファイルをbinファイルに変換するで作成した kv260_mipicsi2_simpleISP.bin ファイルを /lib/firmware/ 以下にコピーします。 USBメモリーなどで開発環境から実機にコピーするか、 scp コマンドなどで実機にコピーしましょう。

同時にdevicetreeの生成で作成した kv260_mipicsi2_simpleISP.dtbo もホームディレクトリにコピーしておきましょう

ubuntu@ubuntu:/media/ubuntu/USB_MEMORY$ sudo cp kv260_mipicsi2_simpleISP.bin /lib/firmware
ubuntu@ubuntu:/media/ubuntu/USB_MEMORY$ sudo cp kv260_mipicsi2_simpleISP.dtbo ~

devicetreeのロード

KV260用のUbuntuイメージは既にFPGA上に回路が書き込まれています。 今回はこの元々書き込まれているFPGAの回路は使わないので、 FPGA上の回路をクリアしたのちISPの回路を書き込みます。 FPGAの回路やISP用のドライバーはdevicetreeで管理されています。 今回のように起動後にシステム構成が変わるような設定を行うためにdevicetree overlayという機能が用います。 まずは元々書き込まれている回路とその回路のドライバーに対応したdevicectree overlayをクリアします。

ubuntu@ubuntu:~$ sudo rmdir /config/device-tree/overlays/k26-starter-kits_image_1

FANのPWM制御をしている配線がなくなるので、FANがうるさくなると思います。 次にISP用のdevicetreeをロードするのですが、 FPGAの回路をロードするドライバが暗号化したbitstreamファイルに対応するモードになっているので、Full Bitstreamモードにしてからdevicetreeをロードします。

ubuntu@ubuntu:~$ echo 1 | sudo tee /sys/class/fpga_manager/fpga0/flags

devicetreeをロードします。

ubuntu@ubuntu:~$ sudo mkdir /config/device-tree/overlays/rpi
ubuntu@ubuntu:~$ sudo cp kv260_mipicsi2_simpleISP.dtbo /config/device-tree/overlays/rpi/dtbo

ISPがLinuxから制御できるか確認しましょう。 以下のコマンドでISPのパイプラインを確認できます。

ubuntu@ubuntu:~$ sudo media-ctl -p

この際にISP内の各画像処理ブロックがv4l-subdevに割り当てられるのですが、 v_gamma_lut 、 v_proc_ss_csc ブロックが v4l-subdevの何番に割り当てられているのかを 記憶しておいてください。

カメラ画像を表示する

ISPを設定するスクリプトを Raspberry Pi Camera-v2 on KV260 に setup_VGA.sh として置いておくのでこれを実行するのですが、 上記の v_gamma_lut 、 v_proc_ss_csc がスクリプト内の設定と異なる番号に 割り当てられることがあるので、スクリプト内の下記の箇所を対応するsubdev番号に書き換えて実行してください。

  • setup_VGA.sh 29行目
yavta --no-query -w '0x0098c9c1 10' /dev/v4l-subdev1 # Red   gain min 1 max 40 step 1 default 10 current 40
yavta --no-query -w '0x0098c9c2 10' /dev/v4l-subdev1 # Blue  gain min 1 max 40 step 1 default 10 current 40
yavta --no-query -w '0x0098c9c3 10' /dev/v4l-subdev1 # Green gain min 1 max 40 step 1 default 10 current 40
  • setup_VGA.sh 37行目

yavta -w '0x0098c9a1 90' /dev/v4l-subdev2 # CSC Brightness' min 0 max 100 step 1 default 50 current 80
yavta -w '0x0098c9a2 50' /dev/v4l-subdev2 # CSC Contrast'   min 0 max 100 step 1 default 50 current 55
yavta -w '0x0098c9a3 35' /dev/v4l-subdev2 # CSC Red Gain'   min 0 max 100 step 1 default 50 current 35 
yavta -w '0x0098c9a4 24' /dev/v4l-subdev2 # CSC Green Gain' min 0 max 100 step 1 default 50 current 24 
yavta -w '0x0098c9a5 40' /dev/v4l-subdev2 # CSC Blue Gain'  min 0 max 100 step 1 default 50 current 40 
ubuntu@ubuntu:~$ bash setup_VGA.sh

画像を表示する用のスクリプトも cap_VGA.sh として置いてあるので、実行してみましょう。

ubuntu@ubuntu:~$ bash cap_VGA.sh

以下は同様の手順でHDで動かしてみた時の画像です。




おわりに

いかがでしたか? 筆者も夏休みモードで執筆していたら、気づけば夏休み後半になっていました。夏の怪談ですね。 夏休みの自由研究と言えば8月30日ぐらいに急いでミニトマトかアサガオを急成長させることが常套手段でしたが、この工作は調達さえ何とかなれば1日で出来てしまいます。今回ご紹介させていただいたISPは絵を作るための最低限の機能とスケーリング機能しか入っていないので、より高画質にする機能や、ぼかし機能、エッジ検出機能を作ってみたり、さらに機能を削ってAI画像認識用の回路を入れてみたりといろんな遊び方を考えられるので、ぜひ冬休みにでも遊んでみてください。

終わりにまた手前味噌ですが、東京大学の高前田伸也准教授と NNgen というAI用の高位合成コンパイラをOSSのプロジェクトとして 共同研究開発しています。 NNgenで生成出来るAI用のアクセラレータIPも今回使用したVivadoから呼び出せる回路として生成することができます。 コラボレーション大歓迎ですので、ご興味ありましたらぜひお問い合わせやPull Requestのほうお待ちしております。

関連リンク

Raspberry Pi Camera v2 on KV260(前編)

  Raspberry Pi Camera v2 on KV260(前編) FPGA初心者の方でも簡単に作れる、KV260を使ったイメージ・シグナル・プロセッサ(ISP)のずぼらレシピ(前編)を紹介したいと思います。 コニカミノルタ株式会社(サイトリニューアル)

Ubuntuイメージ(Xilinx公式)

  Install Ubuntu on Xilinx | Ubuntu Use Ubuntu on Xilinx for the familiar developer experience and an accelerated path to production. Ubuntu

NNgenの紹介

  AIアルゴリズムのハードウェア実装技術 - コニカミノルタのテクノロジー | コニカミノルタ ディープラーニング学習済みモデルのFPGA向け高位合成コンパイラー「NNgen」により、高速・高精度なエッジAIシステムをスピーディーに実現します。 KONICA MINOLTA
  GitHub - NNgen/nngen: NNgen: A Fully-Customizable Hardware Synthesis Compiler for Deep Neural Network NNgen: A Fully-Customizable Hardware Synthesis Compiler for Deep Neural Network - GitHub - NNgen/nngen: NNgen: A Fully-Customizable Hardware Synthesis Compiler for Deep Neural Network GitHub

Raspberry Pi Camera-v2 on KV260

  Build software better, together GitHub is where people build software. More than 83 million people use GitHub to discover, fork, and contribute to over 200 million projects. GitHub

免責事項、著作権、商標権

  サイトのご利用について | コニカミノルタ コニカミノルタのウェブサイトのご利用条件、免責事項、著作権・商標権、推奨環境、アクセシビリティ、個人情報の取り扱い、本ウェブサイトへのリンクについて説明しています。 コニカミノルタ




コニカミノルタは画像IoTプラットフォームFORXAIを通じて、お客様やパートナー様との共創を加速させ、技術・ソリューションの提供により人間社会の進化に貢献してまいります。

Yamano Ryusuke
Yamano Ryusuke
技術開発本部FORXAI開発センター アーキテクチャ開発部 所属 AIアクセラレーターを活用したデバイス開発やNNgenの開発を担当


pagetop