Zybo z7 の Linux で SimpleDRM を実装する

# 目的

Vivado で提供される IP の [Video Frame Buffer Read](https://docs.amd.com/r/ja-JP/ug1449-multimedia/Video-Frame-Buffer-Read) を使い Linux 上で Simple DRM を利用可能にします。

仕様と作業条件は以下の通りです。

* HDMI ポートから画像信号を出力
* 出力画像は 1920x1080x24bit 60Hz
* Vivado で提供される IP のみを使用する
* rootfs は Petalinux で作成

画像処理の部分を AMD 社から提供される IP のみで実現するのが目的です。但し、HDMI ポートへの出力部分だけは Digilent 社の IP を使います。

# 作業結果

結果として SimpleDRM の実装は成功しました。しかし USB マウスの動作が遅いという問題が起きています。その他にも GUI 全体が遅く感じます。

問題の現象は後述します。

# 作業環境
<dl>
    <dt>Bord</dt>
        <dd>Zybo Z7-20</dd>
    <dt>Tool</dt>
        <dd>Vivado ML Edition 2023.2.1 (Windows11)</dd>
        <dd>Petalinux 2023.2.1 (Ubuntu 22.04.4)</dd>
</dl>

# Vivado 作業手順
作業手順は Tcl スクリプトで記述しています。以降ではスクリプトを Vivado 起動後の Tcl console で実行する事を前提に記述します。

## パラメータ設定

プロジェクトを作成するフォルダ名等のパラメータ値を変数に設定します。これらの変数は以降のスクリプトで参照されます。

本記事では c:\vivado\zybo-simpledrm\ 配下にプロジェクトを作成します。

```tcl:
# set project parameters
set PROJ_NAME zybo-simpledrm
set PROJ_ROOT c:/vivado
set PROJ_DIR $PROJ_ROOT/$PROJ_NAME
set PROJ_BD_NAME design_1
set DIGILENT_REPO_NAME vivado-library-master
```

## ボード情報更新

最初に Zybo Z7-20 のボード情報をインストールを実行します。この手順は少し時間が掛かります。

```tcl
# update and install the board information for Zybo z7-20
xhub::refresh_catalog [xhub::get_xstores xilinx_board_store]
set_param board.repoPaths "$env(APPDATA)/Xilinx/Vivado/[version -short]/xhub/board_store/xilinx_board_store"
xhub::install [xhub::get_xitems digilentinc.com:xilinx_board_store:zybo-z7-20:1.1]
```

## Project 作成

先に設定したパラメータに従いプロジェクトを作成します。今回は Digilent 社が提供する IP を利用する為、プロジェクトに IP リポジトリのパスを設定します。本プロジェクトでは **c:\vivado\vivado-library-master** にあるとします。

```tcl
# create a project
file mkdir $PROJ_ROOT
create_project $PROJ_NAME $PROJ_DIR -part xc7z020clg400-1
set_property board_part digilentinc.com:zybo-z7-20:part0:1.1 [current_project]
# register the location for the digilent IP repository
update_ip_catalog -rebuild
set_property ip_repo_paths $PROJ_ROOT/$DIGILENT_REPO_NAME [current_project]
update_ip_catalog
```

`vivado-library-master` は GitHub から入手可能です。
https://github.com/Digilent/vivado-library

## Block Design 作成

### 表示部の基本構成

今回作成する Framebuffer 表示部の基本構成は以下の通りです。

![video.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/f2000366-8f2b-7ef2-33b1-86335a2b5b6a.png)


これらに必要な設定を行った後、Clock や AXI bus を繋いで行きます。

Block Design 上にある左側の IP から順に配置して行きます。

### Video Frame Buffer Read 追加

この IP は RAM 上にある Framebuffer の情報を読み出して AXI4-Stream 形式に変換します。

```tcl
# create a block design
create_bd_design $PROJ_BD_NAME
# add VideoFrameBufferRead
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:v_frmbuf_rd:2.5 v_frmbuf_rd_0
set_property CONFIG.SAMPLES_PER_CLOCK {1} [get_bd_cells v_frmbuf_rd_0]
endgroup
```

クロック単位でのサンプル数を１ピクセルに変更しています。変更の理由は単に処理が間に合うのか否かを確認したかっただけです。画像フォーマットのデフォルト設定が RGB8 なので、全ての設定はデフォルトのまま使用します。

![bd-fb-vr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/3cc06bd5-c788-44d8-14d2-3d1ce6a440e6.png)

### AXI4-Stream to Vide Out 追加

これは AXI4-Stream 形式のデータを 24-bit の RGB 信号に変換します。このIPは Video Frame Buffer Read と接続するのですが、Linux の Simple DRM が扱う RGB データと、これらの IP が扱う RGB データはフォーマットが異なっている様で、このまま使うと色が正常に出力されません。

この問題解決の為に Slice と Concat を使い IP 間の tdata 信号を強引に変換しています。

先ずは信号を変換する為の Slice と Concat を追加します。

```tcl
# add Slice
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 xlslice_0
set_property -dict [list \
  CONFIG.DIN_FROM {7} \
  CONFIG.DIN_TO {0} \
  CONFIG.DIN_WIDTH {24} \
] [get_bd_cells xlslice_0]
create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 xlslice_1
set_property -dict [list \
  CONFIG.DIN_FROM {15} \
  CONFIG.DIN_TO {8} \
  CONFIG.DIN_WIDTH {24} \
] [get_bd_cells xlslice_1]
create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 xlslice_2
set_property -dict [list \
  CONFIG.DIN_FROM {23} \
  CONFIG.DIN_TO {16} \
  CONFIG.DIN_WIDTH {24} \
] [get_bd_cells xlslice_2]
endgroup
# add Concat and connect to Slice
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 xlconcat_0
set_property CONFIG.NUM_PORTS {3} [get_bd_cells xlconcat_0]
connect_bd_net [get_bd_pins xlslice_0/Dout] [get_bd_pins xlconcat_0/In0]
connect_bd_net [get_bd_pins xlslice_1/Dout] [get_bd_pins xlconcat_0/In2]
connect_bd_net [get_bd_pins xlslice_2/Dout] [get_bd_pins xlconcat_0/In1]
endgroup
```

この時点では Concat の入出力ポートのビット幅が合っていませんが、後程自動で設定されます。

次に AXI4-Stream to Vide Out を追加して Video Frame Buffer Read と接続します。

```tcl
# add AXI4-StreamToVideoOut
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:v_axi4s_vid_out:4.0 v_axi4s_vid_out_0
set_property CONFIG.C_HAS_ASYNC_CLK {1} [get_bd_cells v_axi4s_vid_out_0]
connect_bd_net [get_bd_pins v_frmbuf_rd_0/m_axis_video_TDATA] [get_bd_pins xlslice_0/Din]
connect_bd_net [get_bd_pins v_frmbuf_rd_0/m_axis_video_TDATA] [get_bd_pins xlslice_1/Din]
connect_bd_net [get_bd_pins v_frmbuf_rd_0/m_axis_video_TDATA] [get_bd_pins xlslice_2/Din]
connect_bd_net [get_bd_pins xlconcat_0/dout] [get_bd_pins v_axi4s_vid_out_0/s_axis_video_tdata]
connect_bd_net [get_bd_pins v_frmbuf_rd_0/m_axis_video_TLAST] [get_bd_pins v_axi4s_vid_out_0/s_axis_video_tlast]
connect_bd_net [get_bd_pins v_frmbuf_rd_0/m_axis_video_TREADY] [get_bd_pins v_axi4s_vid_out_0/s_axis_video_tready]
connect_bd_net [get_bd_pins v_frmbuf_rd_0/m_axis_video_TUSER] [get_bd_pins v_axi4s_vid_out_0/s_axis_video_tuser]
connect_bd_net [get_bd_pins v_frmbuf_rd_0/m_axis_video_TVALID] [get_bd_pins v_axi4s_vid_out_0/s_axis_video_tvalid]
endgroup
```

AXI4-Stream to Vide Out は Clock mode を Independent に変更して、後はデフォルトのまま使用します。

![bd-fb-stov.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/356e9d9b-0be1-615e-84ef-b4c70c86deb8.png)

### RGB2DVI 追加

RGB2DVI は Digilent 社から提供される IP です。これは出力解像度に応じて TMDS clock range を設定する必要がありますが、デフォルトが 1080p 用の設定なので、今回はデフォルト設定のまま使用します。

```tcl
# add RGB2DVI
startgroup
create_bd_cell -type ip -vlnv digilentinc.com:ip:rgb2dvi:1.4 rgb2dvi_0
apply_board_connection -board_interface "hdmi_out" -ip_intf "rgb2dvi_0/TMDS" -diagram $PROJ_BD_NAME 
connect_bd_intf_net [get_bd_intf_pins rgb2dvi_0/RGB] [get_bd_intf_pins v_axi4s_vid_out_0/vid_io_out]
endgroup
```

![bd-fb-r2d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/c69812c0-0bbc-6a87-8838-639a6dab905a.png)

### Video Timing Controller 追加

この IP は解像度に応じた同期信号等を生成します。

```tcl
# add VideoTimingController
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:v_tc:6.2 v_tc_0
set_property -dict [list \
  CONFIG.HAS_AXI4_LITE {false} \
  CONFIG.enable_detection {false} \
  CONFIG.VIDEO_MODE {1080p} \
] [get_bd_cells v_tc_0]
connect_bd_net [get_bd_pins v_tc_0/gen_clken] [get_bd_pins v_axi4s_vid_out_0/vtg_ce]
connect_bd_intf_net [get_bd_intf_pins v_tc_0/vtiming_out] [get_bd_intf_pins v_axi4s_vid_out_0/vtiming_in]
endgroup
```

固定解像度の信号生成のみで使用するので AXI4-Lite I/F や Detection option は無効化します。

![bd-fb-tc-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/536d0bcf-815c-8250-31ef-814bdaf8ca2c.png)
![bd-fb-tc-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/f53fd2d1-72d5-da5d-a428-a601b683cb97.png)
![bd-fb-tc-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/f37e16d9-4450-2617-92b2-ca4aad2f3912.png)

### Pixel clock 追加

Clocking Wizard で pixel clock を生成して各 IP に接続します。ボード上に実装されている 125MHz のクロックを pixel clock の生成源とします。今回は 1920x1080 60fps 固定なので 148.5MHz を１つ生成します。その他の設定はデフォルトのまま使用します。

```tcl
# add pixel clock generator
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:clk_wiz:6.0 clk_wiz_0
apply_board_connection -board_interface "sys_clock" -ip_intf "clk_wiz_0/clock_CLK_IN1" -diagram $PROJ_BD_NAME 
set_property -dict [list \
  CONFIG.CLKOUT1_JITTER {216.859} \
  CONFIG.CLKOUT1_PHASE_ERROR {245.344} \
  CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {148.5} \
  CONFIG.MMCM_CLKFBOUT_MULT_F {37.125} \
  CONFIG.MMCM_CLKOUT0_DIVIDE_F {6.250} \
  CONFIG.MMCM_DIVCLK_DIVIDE {5} \
] [get_bd_cells clk_wiz_0]
endgroup
# connect the pixel clock to video IPs
startgroup
connect_bd_net [get_bd_pins clk_wiz_0/clk_out1] [get_bd_pins rgb2dvi_0/PixelClk]
connect_bd_net [get_bd_pins clk_wiz_0/clk_out1] [get_bd_pins v_tc_0/clk]
connect_bd_net [get_bd_pins clk_wiz_0/locked] [get_bd_pins v_tc_0/clken]
connect_bd_net [get_bd_pins clk_wiz_0/clk_out1] [get_bd_pins v_axi4s_vid_out_0/vid_io_out_clk]
connect_bd_net [get_bd_pins clk_wiz_0/locked] [get_bd_pins v_axi4s_vid_out_0/vid_io_out_ce]
endgroup
```

### ZYNQ-7000 追加
ZYNQ-7000 を下記設定で追加します。
* FCLK_CLK0 - 200MHz
* S_AXI_HP0 - enabled

FCLK＿CLK0 は AXI Bus のクロックとして利用します。表示部の pixel clock が 148.5MHz なので、これ以上の速さで framebuffer を読み込ませる為に、FCLK_CLK0 を 200MHz に設定しています。

```tcl
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0
set_property -dict [list \
  CONFIG.PCW_FPGA0_PERIPHERAL_FREQMHZ {200} \
  CONFIG.PCW_USE_S_AXI_HP0 {1} \
] [get_bd_cells processing_system7_0]
apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 -config {make_external "FIXED_IO, DDR" apply_board_preset "1" Master "Disable" Slave "Disable" }  [get_bd_cells processing_system7_0]
endgroup
```

### AXI bus と reset 信号を接続

各モジュールの AXI I/F と reset 信号を自動的に接続します。AXI4-Stream to Vide Out の aclk と aresetn もここで接続します。

```tcl
# add ProcessorSystemReset
create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset:5.0 proc_sys_reset_0
# connect aclk and aresetn for AXI4StreamToVideOut
startgroup
connect_bd_net [get_bd_pins proc_sys_reset_0/peripheral_aresetn] [get_bd_pins v_axi4s_vid_out_0/aresetn]
connect_bd_net [get_bd_pins processing_system7_0/FCLK_CLK0] [get_bd_pins v_axi4s_vid_out_0/aclk]
endgroup
# automatically connect AXI buses and resets
startgroup
apply_bd_automation -rule xilinx.com:bd_rule:board -config { Manual_Source {Auto}}  [get_bd_pins proc_sys_reset_0/ext_reset_in]
apply_bd_automation -rule xilinx.com:bd_rule:clkrst -config { Clk {/processing_system7_0/FCLK_CLK0 (200 MHz)} Freq {100} Ref_Clk0 {} Ref_Clk1 {} Ref_Clk2 {}}  [get_bd_pins proc_sys_reset_0/slowest_sync_clk]
apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/v_frmbuf_rd_0/m_axi_mm_video} Slave {/processing_system7_0/S_AXI_HP0} ddr_seg {Auto} intc_ip {New AXI Interconnect} master_apm {0}}  [get_bd_intf_pins processing_system7_0/S_AXI_HP0]
apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/processing_system7_0/M_AXI_GP0} Slave {/v_frmbuf_rd_0/s_axi_CTRL} ddr_seg {Auto} intc_ip {New AXI Interconnect} master_apm {0}}  [get_bd_intf_pins v_frmbuf_rd_0/s_axi_CTRL]
endgroup
```

### その他、未接続の信号を接続

reset 等まだ接続していない信号を接続します。ここでは信号を HIGH か LOW のどちらかに固定します。接続しなくても動作する信号もありますが、万が一動作しない場合に疑う箇所が増えるので、保険の意味で接続しています。

```tcl
# set signals to LOW
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 fixed_low
set_property CONFIG.CONST_VAL {0} [get_bd_cells fixed_low]
connect_bd_net [get_bd_pins fixed_low/dout] [get_bd_pins clk_wiz_0/reset]
connect_bd_net [get_bd_pins fixed_low/dout] [get_bd_pins v_axi4s_vid_out_0/vid_io_out_reset]
connect_bd_net [get_bd_pins fixed_low/dout] [get_bd_pins rgb2dvi_0/aRst]
endgroup
# set signals to HIGH
startgroup
create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 fixed_high
connect_bd_net [get_bd_pins fixed_high/dout] [get_bd_pins v_axi4s_vid_out_0/aclken]
connect_bd_net [get_bd_pins fixed_high/dout] [get_bd_pins v_tc_0/resetn]
endgroup
```

### デバッグ用信号を LED に接続

デバッグ用に幾つかの信号を LED に接続します。

```tcl
startgroup
create_bd_port -dir O LED_LD0
connect_bd_net [get_bd_pins /v_axi4s_vid_out_0/underflow] [get_bd_ports LED_LD0]
create_bd_port -dir O LED_LD1
connect_bd_net [get_bd_pins /v_axi4s_vid_out_0/overflow] [get_bd_ports LED_LD1]
create_bd_port -dir O LED_LD2
connect_bd_net [get_bd_pins /v_axi4s_vid_out_0/locked] [get_bd_ports LED_LD2]
create_bd_port -dir O LED_LD3
connect_bd_net [get_bd_pins /clk_wiz_0/locked] [get_bd_ports LED_LD3]
endgroup
```

Clocking Wizard と AXI4-Stream to Vide Out の Lock 信号を LED2/LED3 に繋ぎ、エラー状態を示す信号を LED0/LED1 に接続しています。画面出力が正常に行われると各 LED は以下の状態になります。

|LED3|LED2|LED1|LED0|
|:--:|:--:|:--:|:--:|
|ON  |ON  |OFF |OFF |

ここまの作業で Block Design の作成は終了です。以下の様な形で出来上がります。

![BlockDesign.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/b81d0886-edf2-5fdf-213d-1e26594a1c64.png)


## HDL wrapper 作成

作成した Block Design のラッパーファイルを作成します。

```tcl
make_wrapper -files [get_files $PROJ_DIR/${PROJ_NAME}.srcs/sources_1/bd/$PROJ_BD_NAME/${PROJ_BD_NAME}.bd] -top
add_files -norecurse $PROJ_DIR/${PROJ_NAME}.gen/sources_1/bd/$PROJ_BD_NAME/hdl/${PROJ_BD_NAME}_wrapper.v
```

## 制約ファイルの追加

プロジェクトフォルダ直下に zybo-simpledrm.xdc を下記内容で作成します。

```:zybo-simpledrm.xdc
# HDMI Tx (TMDS only)
set_property PACKAGE_PIN H16 [get_ports hdmi_out_clk_p]
set_property PACKAGE_PIN B19 [get_ports {hdmi_out_data_p[2]}]
set_property PACKAGE_PIN C20 [get_ports {hdmi_out_data_p[1]}]
set_property PACKAGE_PIN D19 [get_ports {hdmi_out_data_p[0]}]

# for debugging
set_property IOSTANDARD LVCMOS33 [get_ports LED_LD0]
set_property IOSTANDARD LVCMOS33 [get_ports LED_LD1]
set_property IOSTANDARD LVCMOS33 [get_ports LED_LD2]
set_property IOSTANDARD LVCMOS33 [get_ports LED_LD3]
set_property PACKAGE_PIN M14 [get_ports LED_LD0]
set_property PACKAGE_PIN M15 [get_ports LED_LD1]
set_property PACKAGE_PIN G14 [get_ports LED_LD2]
set_property PACKAGE_PIN D18 [get_ports LED_LD3]
```

作成した制約ファイルをプロジェクトに追加します。

```tcl
add_files -fileset constrs_1 -norecurse $PROJ_DIR/${PROJ_NAME}.xdc
```

## Bitstream ＆ XSA ファイル作成

論理合成と配置配線を実行して、ビットストリームと XSA ファイルを作成します。

```tcl
launch_runs impl_1 -to_step write_bitstream -jobs 4
wait_on_run impl_1
write_hw_platform -fixed -include_bit -force -file ${PROJ_DIR}/${PROJ_NAME}.xsa
```

XSA ファイルが作成できたら Vivado での作業は終了です。

# Petalinux ビルド手順

作成した XSA ファイルを使い petalinux の実行環境を作成します。以降では Linux の Shell 上で実行するコマンドや各種ファイルの編集について記述します。

## プロジェクト作成

ここではホームディレクトリ配下に simpledrm という名称でプロジェクトを作成します。

```bash
petalinux-create -t project --template zynq -n zybo-simpledrm
```

## ハードウエア情報取得

Vivado で作成した zybo-simpledrm.xsa ファイルをプロジェクトに反映させます。XSA ファイルはホームディレクトリ配下にある事を前提とします。

```bash
cd ~/zybo-simpledrm
petalinux-config --get-hw-description ~/zybo-simpledrm.xsa --silentconfig
```

## Kernel コンフィグレーション

Kernel option の Simple DRM を有効化する **project-spec/meta-user/recipes-kernel/linux/linux-xlnx/simpledrm.cfg** を作成します。

```:project-spec/meta-user/recipes-kernel/linux/linux-xlnx/simpledrm.cfg
CONFIG_APERTURE_HELPERS=y
CONFIG_DRM_GEM_SHMEM_HELPER=y
CONFIG_DRM_SIMPLEDRM=y
```

次に **project-spec/meta-user/recipes-kernel/linux/linux-xlnx_%.bbappend** に下記を追記して simpledrm.cfg を有効にします。

```:project-spec/meta-user/recipes-kernel/linux/linux-xlnx_%.bbappend 追記
SRC_URI:append = " file://simpledrm.cfg"
```
:::note
追記内容はファイルの末尾に追加して下さい。以降、"追記"と記載した場合は同様にファイル末尾への追加とします。
:::

## Devicetree 編集

Framebuffer 領域の定義と USB に関する修正を Devicetree に行います。

**project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi** を下記の様に修正して下さい。

```:project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
/include/ "system-conf.dtsi"

/delete-node/ &v_frmbuf_rd_0;

/ {
        memory@0 {
                reg = <0x0 0x3f800000>;
        };

        framebuffer@3f800000 {
                compatible = "simple-framebuffer";
                reg = <0x3f800000 0x800000>;
                width = <1920>;
                height = <1080>;
                stride = <(1920 * 3)>;
                format = "r8g8b8";
        };

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

&usb0 {
        dr_mode = "host";
        usb-phy = <&usb_phy0>;
};
```

Video Frame Buffer Read 用に意図しないドライバがインストールされるのを防ぐ目的で v_frmbuf_rd_0 の定義を削除しています。

今回は RAM 領域の 0x3F800000 - 0x3FFFFFFF を Framebuffer として利用します。その為 memory 定義を修正しています。

framebuffer で定義している各パラメータは後程記載する`Framebuffer 初期化処理追加`で設定するパラメータと一致させる必要があります。

USB に関しては Zybo で利用する時のおまじないと思って下さい。ここでは説明を省略します。ググれば情報が出て来ますが、Qiita では [@ikwzm](https://qiita.com/ikwzm) さんの記事を参考にしました。

https://qiita.com/ikwzm/items/0ef8546a2709428e515b

USB にはもう１つ注意点があります。 USB ポートを利用する際は、ポート横にあるジャンパーピン **JP2 をショート**して下さい。これがオープンになっているとポートからの電源供給が行われず、デバイスが認識されません。私はこれで１時間ほど悩んでしまいました。

## rootfs コンフィグレーション

Simple DRM で動作させるアプリケーションとして下記パッケージグループを rootfs に追加します。

* Qt/Qt Extended
* X11/matchbox

これらを追加する為に **project-spec/configs/rootfs_config** へ下記を追記します。パッケージグループの追加だけでは起動しなかったので、２つ程ライブラリを追加しています。

```:project-spec/configs/rootfs_config 追記
CONFIG_packagegroup-petalinux-qt=y
CONFIG_packagegroup-petalinux-qt-extended=y
CONFIG_packagegroup-petalinux-matchbox=y
CONFIG_packagegroup-petalinux-x11=y
CONFIG_xserver-xorg-extension-glx=y
CONFIG_xf86-video-modesetting=y
```

Petalinux のデフォルト設定では rootfs として ramfs が使われます。rootfs を SD Card にしたい場合は下記設定を **project-spec/configs/config** に追記して下さい。

```:project-spec/configs/config 追記
CONFIG_SUBSYSTEM_INITRD_RAMDISK_LOADADDR=n
CONFIG_SUBSYSTEM_INITRAMFS_IMAGE_NAME=n
CONFIG_SUBSYSTEM_ROOTFS_EXT4=y
CONFIG_SUBSYSTEM_SDROOT_DEV="/dev/mmcblk0p2"
CONFIG_SUBSYSTEM_BOOTARGS_GENERATED="console=ttyPS0,115200 earlycon root=/dev/mmcblk0p2 ro rootwait"
```

## Framebuffer 初期化処理追加

Simple DRM driver では H/W の初期化処理は行いませんので、Video Frame Buffer Read を何処かで初期化する必要があります。

今回は u-boot のブートスクリプト (boot.scr) 内で Linux 起動前に初期化します。

下記内容で **project-spec/meta-user/recipes-bsp/u-boot/u-boot-xlnx-scr.bbappend** を作成します。

```:project-spec/meta-user/recipes-bsp/u-boot/u-boot-xlnx-scr.bbappend
PRE_BOOTENV:append := "\n#### initialize VideoFrameBufferRead ####\n"
PRE_BOOTENV:append := "mw.l 43c00010 780       # 1920\n"
PRE_BOOTENV:append := "mw.l 43c00018 438       # 1080\n"
PRE_BOOTENV:append := "mw.l 43c00020 1680      # 1920 * 3\n"
PRE_BOOTENV:append := "mw.l 43c00028 14        # RGB8\n"
PRE_BOOTENV:append := "mw.l 43c00030 3f800000  # framebuffer\n"
PRE_BOOTENV:append := "mw.l 43c00000 81        # start"
```

実行内容は mw コマンドを使って Video Frame Buffer Read のレジスタを直接初期化しているだけです。初期化後は画面にゴミが表示されますが、これは Framebuffer 領域を初期化していない事が原因です。気になる人は Framebuffer 領域の初期化処理も入れてみて下さい。

ここで設定するパラメータは Devicetree に記載している framebuffer のパラメータと一致する必要があります。

## ビルド

設定が終了したらビルドを実行します。ビルド終了後は petalinux-package で BOOT.BIN を作成します。

```bash
petalinux-build
petalinux-package --boot --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot --force
```

# Petalinux 起動

作成した BOOT.BIN, boot.scr, image.ub を SD Card に書き込んでボードの電源を投入します。SD Card を rootfs に指定している場合は rootfs.tar.gz も SD Card 上に展開して下さい。

起動時に SSH の Host Key を作成するので少し時間が掛かります。rootfs を SD Card にしている場合、時間が掛かるのは最初だけです。

Petalinux が起動して LED3/LED2 が点灯、LED1/LED0 が消灯状態になれば成功です。

# Qt で動作確認

設定が成功していれば matchbox が起動しているはずです。この状態から Qt アプリケーションを起動します。動きのあるサンプルとして `animatedtiles` を実行します。ユーザー名 `petalinux` でログインしてから、下記コマンドを実行して下さい。

```bash
export DISPLAY=:0.0
cd /usr/share/examples/widgets/
./animation/animatedtiles/animatedtiles
```

実行画面はこんな感じです。

![QtDemo.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332407/9c4d3723-8bf9-0738-7e61-686b632031f7.jpeg)

この他にも `/usr/share/examples` 配下に色々と用意されているので試してみて下さい。 

# 【問題点】 マウスカーソルの動きが鈍い

USB Mouse を使うとカーソルの動きが異様に遅い事が気になります。Qt アプリケーションが動作している時にマウスカーソルを動かすと、アプリ全体の動作が遅くなります。

全体的に GUI が遅いのも気になります。Framebuffer のみを使う単純な構成が原因なのか、今回利用している解像度を Zynq-7000 で扱うには負荷が高いのか、現時点では解っていません。

そもそもアプリケーション起動時にエラーが幾つか出ますので、不足しているライブラリ等がありそうです。

一応 Qt が起動しているという事で、細かい所までは追っていません。

# まとめ

マウスの問題が解決していないので少し気持ち悪さが残りますが、目標である SimpleDRM の実装は実現出来たと思います。

慣れない作業という事もあり rootfs を構築するのが大変でした。FPGA でシステム構築する場合このスキルも必要かと思いますが、ハードルが高いです。