Zedboardで、最小構成のPSでubuntuを動かす - kernel, device tree, u-bootのビルドからHello worldまで

  • 9
    Like
  • 0
    Comment
More than 1 year has passed since last update.

Zed boardのプリセット設定のPSのみをインプリメントしたZynq上でubuntuを動かします。
勉強のために、リファレンスデザインの改造などでなく、一からハードウェアの設計(PSをzed boardプリセット設定にするだけですが笑)、Linuxカーネル、デバイスツリー、u-bootのビルドをやってみました。
映像出力用のIPなど、PS以外のあらゆるハードウェアは含めないので、キーボードやディスプレイは使用できません。シリアルコンソールで操作します。

ディスプレイ、キーボードを使用できるリファレンスデザインについては http://qiita.com/yuichiroTCY/items/3b792feedb8f55aaef43 を書きました。
そちらのADV7511リファレンスデザインなどを見ながら改造すると良いと思います。
また、この内容がかぶる部分が結構あります。
合わせて読むと良いかもしれません。

環境

以下の環境で試しました。

  • xubuntu 14.04
  • vivado 2015.2

ハードウェア

vivadoでハードウェアを設計、ビットストリームを作成します。
といっても、普通にZedboardプリセット設定のPSのみのデザインを作成するだけです。特別なことはありません。
一応手順は書いておきますが、本当に普通です。

  1. 新規プロジェクトを作成。プロジェクト設定ダイアログのDefault PartページでZed boardを選んでおく。
    dialog.png

  2. Create Block Design. Design nameはsystemとした。

  3. Add IP -> ZYNQ7 Processing System

  4. 追加されたZynqコアをダブルクリックして設定ダイアログを表示

  5. PresetsからZed boardを選択、OK

  6. Run Block Automationを実行

  7. FCLK_CLK0とM_AXI_GP0_ACLKを接続

  8. Tool -> Validate Designを実行し、エラーが無いことを確認

  9. Sourcesペインでsystemを右クリック -> Generate Output Products

  10. Sourcesペインでsystemを右クリック -> Create HDL Wrapper

  11. Generate Bitstream

ちなみに最終的なデザインはこんな感じです。本当に最小限。
design.png

必要なソースのダウンロード

ここから一旦Vivadoを離れてシェルで作業します。
適当な作業ディレクトリに移動し、以下のようにXilinxのリポジトリから必要なソースを落としてきます。

$ git clone git@github.com:Xilinx/linux-xlnx.git
$ git clone git@github.com:Xilinx/u-boot-xlnx.git
$ git clone git@github.com:Xilinx/device-tree-xlnx.git

u-bootのビルド

u-boot-xlnxディレクトリに移動し、適当なリビジョンをチェックアウトします。

$ cd u-boot-xlnx
$ git checkout xilinx-v2015.2

zynq_zed.hの編集

include/configs/zynq_zed.hを編集します。
include/configs/zynq-common.hの内容が基本的な設定で、これがzynq_zed.hにインクルードされ、さらにその中でオーバーライドされます。
zynq_zed.hを編集してさらに必要な設定をオーバーライドします。
今回、ext4パーティション上にRoot file systemを用意してLinuxを立ち上げるので、そのための設定のようです。

  1. include/configs/zynq-common.hからCONFIG_EXTRA_ENV_SETTINGSの定義(複数行の長い#define)をコピーして include/configs/zynq_zed.hの末尾に貼り付け。
  2. sdbootでgrepし、当該部分を書き換え

    "sdboot=if mmcinfo; then " \
                "run uenvboot; " \
    -           "echo Copying Linux from SD to RAM... && " \
    +           "echo Copying Linux from SD to RAM... RFS in ext4 && " \
                "load mmc 0 ${kernel_load_address} ${kernel_image} && " \
                "load mmc 0 ${devicetree_load_address} ${devicetree_image} && " \
    -           "load mmc 0 ${ramdisk_load_address} ${ramdisk_image} && " \
    -           "bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}; " \
    +           "bootm ${kernel_load_address} - ${devicetree_load_address}; " \
    
  3. #define CONFIG_EXTRA_ENV_SETTINGSの前に

    #ifdef CONFIG_EXTRA_ENV_SETTINGS 
    #undef CONFIG_EXTRA_ENV_SETTINGS 
    #endif
    

    を追記

  4. (Optional) 自分の好みに応じて、同様に#define CONFIG_SYS_PROMPT#define CONFIG_BOOTDELAYなどをオーバーライドしてもよい

ビルド

$ export CROSS_COMPILE=arm-xilinx-linux-gnueabi-
$ make distclean
$ make zynq_zed_config
$ make

ビルドが完了すると、u-boot-xlnxディレクトリ直下にu-bootが生成されます。

また、tools/mkimageというファイルもできています。あとで使うので、パスを通しておきます。

$ export PATH=/path/to/dir/u-boot-xlnx/tools/:${PATH}

Linuxカーネルのビルド

linux-xlnxディレクトリに移動し、適当なリビジョンをチェックアウトします。
その後、ビルドします。

$ cd linux-xlnx
$ git checkout xilinx-v2015.2
$ make ARCH=arm xilinx_zynq_defconfig
$ make ARCH=arm UIMAGE_LOADADDR=0x8000 uImage

前章で設定したCROSS_COMPILE環境変数がこのビルドでも必要です。
また、mkimageへパスが通っていることも必要です。
エラーが出る場合はこの辺を確認してください。

SDKでの作業

Vivadoに戻り、Export -> Export Hardwareのあと、Launch SDKでSDKを立ち上げます。

FSBL作成

FSBLを作成します。特に変わったことはありません。
File -> New -> Application Projectを選択します。

New Projectダイアログで、Project nameは適当にzynq_fsbl_0とかにしておきます。
Finishは押さずにNext。
2ページ目でZynq FSBLを選び、Finish。

BOOT.bin作成

  1. ツリービューでzynq_fsbl_0を選択した状態で、メニューからXilinx Tools -> Create Zynq Boot Imageを選びます。
  2. Output BIF file pathを適宜設定します。今回はファイル名をubuntu.bifに変えました。
  3. ダイアログ下部のBoot image partitionsにはすでにbootloaderとビットストリームが読み込まれているはずです。無ければ、適宜Addします。
  4. 上で作ったu-bootをu-boot.elfとリネームして適当な場所にコピーしておきます。Boot image partitionsにAddします。Partition typeはdatafileです。
  5. 適宜Output pathを設定します。
  6. Create Imageを押します。Output pathで指定した場所にBOOT.binができます。

Device tree作成

まず、Device tree generatorをSDKに追加します。

  1. Xilinx Tools -> Repositories を選択
  2. Local RepositoriesでNewを押し、上でダウンロードしたdevice-tree-xlnxディレクトリを選択し追加
  3. OKを押してダイアログを抜ける。

次に、Device tree BSPを作成します。

  1. File -> New -> Board Support Package を選択
  2. Board Support Package OSの欄でdevice_treeを選択
  3. Finish
  4. 次に開く設定ダイアログで、bootargs行のValue列にconsole=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=1と入力
  5. OK

以上でsystem.dtsファイルが作成されます。
ちなみに、上記bootargsなどを変えたいときは、system.mssをダブルクリックして表示される画面でModify this BSP's Settingsを押して設定を変更したあと、Re-generate BSP Sourcesでファイルを更新します。

最後に、.dtsファイルをコンパイラに食わせて.dtbファイルを作成します。
コンパイラはlinux-xlnx/scripts/dtc/dtcにあります。
シェルで作業します。

$ cd path/to/sdk/device_tree_bsp_0
$ path/to/linux-xlnx/scripts/dtc/dtc -I dts -O dtb -o devicetree.dtb system.dts

これでdevicetree.dtbができました。

SDカードの準備

fdiskやGPartedでFAT32とext4のパーティションを1つずつ作ります。
FAT32の方をZED_BOOT、ext4の方をrootfsと名づけました。

Root file system作成

$ wget http://releases.linaro.org/15.04/ubuntu/utopic-images/nano/linaro-utopic-nano-20150421-702.tar.gz

他のイメージを使いたい場合、
http://www.linaro.org/downloads/historic/
から好きなのを探してください。
あまり古いとサポートが切れていてaptのリポジトリがなくなっていたりするのでおすすめしません。設定をいじる場所が増えます。

SDカードへ展開

SDカードをPCに挿し、マウントした状態で以下を実行します。
/media/foo/rootfsはSDカードのrootfsパーティションがマウントされた場所です。自分の環境に合わせて適宜変えます。

$ sudo tar --strip-components=1 -C /media/foo/rootfs -xzpf linaro-saucy-nano-20140410-652.tar.gz

わりと時間がかかります。
また、展開後は

$ sudo sync

を実行します。(メモリ上のキャッシュをすべてSDに書き込んでいる?)結構時間がかかります。
もしくは、GUIでSDカードのマウントを解除してもよいです。多分内部でsyncしているので、それでも時間はかかります。

BOOT.bin, uImage, devicetree.dtbをSDカードにコピー

linux-xlnx/arch/arm/boot/uImage, path/to/sdk/zynq_fsbl_0/bootimage/BOOT.bin, path/to/sdk/device_tree_bsp_0/devicetree.dtbをSDカードのFATフォーマットのパーティションにコピーします。

Ubuntu起動

作成したSDカードをZed boardに挿し、電源を入れるとubuntuが起動します。
シリアルコンソールから確認します。

u-bootの環境変数がうまく設定されていないと、うまく起動しなかったり、u-bootが入力受付状態で止まったりします。
u-bootが入力受付状態になればそのまま、
そうならなければZed boardのPS-RSTボタンを押してブートをやり直し、u-bootがlinuxのブートを始める前に何らかのキー入力を与えて中断し、u-bootを入力受付状態にします。
その後、u-bootのコンソールで

env default –a –f
printenv sdboot
saveenv

を入力します。
printenv sdbootで、上記でzynq_zed.hに対して行った変更が反映されていることを確認してください。
その後、resetコマンドを入力するとu-bootが再ブートされます。

ネットワークの設定など
http://qiita.com/yuichiroTCY/items/3b792feedb8f55aaef43
も読んでください。

AXIバスにIPを追加してみる

IPを追加してLinuxに認識させてみます。

PLデザイン

design2.png
こんな感じで、AXI GPIOを2つ追加し、それぞれLEDとスイッチにつながります。
address.png
アドレスマッピングはこんな感じ。

ハードウェアは以上。ビットストリームを作成し、SDKにエクスポートします。

BOOT.bin, Device tree再生成

ビットストリームが新しくなったので、BOOT.binを再生成します。
上記のBOOT.bon作成をもう一度やればOKです。

また、Device treeも再生成します。
Device tree BSPプロジェクト(デフォルトならdevice_tree_bsp_0)の下にあるsystem.mssをダブルクリックします。
開いたペインで、Re-generate BSP Sourcesをクリックします。
Device treeが再生成されます。
ここで、pl.dtsiを確認してみると、

/ {
    amba_pl: amba_pl {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "simple-bus";
        ranges ;
        axi_gpio_0: gpio@41200000 {
            #gpio-cells = <2>;
            compatible = "xlnx,xps-gpio-1.00.a";
            gpio-controller ;
            reg = <0x41200000 0x10000>;
            xlnx,all-inputs = <0x0>;
            xlnx,all-inputs-2 = <0x0>;
            xlnx,all-outputs = <0x1>;
            xlnx,all-outputs-2 = <0x0>;
            xlnx,dout-default = <0x00000000>;
            xlnx,dout-default-2 = <0x00000000>;
            xlnx,gpio-width = <0x8>;
            xlnx,gpio2-width = <0x20>;
            xlnx,interrupt-present = <0x0>;
            xlnx,is-dual = <0x0>;
            xlnx,tri-default = <0xFFFFFFFF>;
            xlnx,tri-default-2 = <0xFFFFFFFF>;
        };
        axi_gpio_1: gpio@41210000 {
            #gpio-cells = <2>;
            compatible = "xlnx,xps-gpio-1.00.a";
            gpio-controller ;
            reg = <0x41210000 0x10000>;
            xlnx,all-inputs = <0x1>;
            xlnx,all-inputs-2 = <0x0>;
            xlnx,all-outputs = <0x0>;
            xlnx,all-outputs-2 = <0x0>;
            xlnx,dout-default = <0x00000000>;
            xlnx,dout-default-2 = <0x00000000>;
            xlnx,gpio-width = <0x8>;
            xlnx,gpio2-width = <0x20>;
            xlnx,interrupt-present = <0x0>;
            xlnx,is-dual = <0x0>;
            xlnx,tri-default = <0xFFFFFFFF>;
            xlnx,tri-default-2 = <0xFFFFFFFF>;
        };
    };
};

となっていて、追加したIPが正しく反映されていることがわかります。

Device treeも、上記の手順を再度繰り返してコンパイルし、devicetree.dtbを再生成します。

SDカードへコピー

生成したファイルをSDカードにコピーします。
最初に作成したSDカードを再び使うのであれば、更新されたBOOT.bindevicetree.dtbだけ上書きすればOKです。

テスト

SDブートし、ubuntuを起動します。
devmem2をインストールします。(ネットワーク設定などは完了していて、apt-getが使えるものとします)

$ apt-get install devmem2

/dev/memという特殊なデバイスファイルを使うと直接アドレス空間にアクセスできるため、
デバイスドライバ不要でIPのレジスタに書き込み・読み出しができます。
devmem2はこれをやってくれるコマンドです。

今回は簡単のため/dev/memを使ってしまいますが、
本来は独自ハードウェアはそれに対応したデバイスドライバを作成する必要があります。
本番では/dev/memは使わないようにしましょう。

また、/dev/memを利用するにはroot権限が必要です。

$ devmem2 0x41200000 b 0xff

とすると、LEDが点灯します。アドレス0x41200000に、8bit幅(b)で、値0xffを書き込みました。

また、適当にスイッチを設定して、

$ devmem2 0x41210000 b

とすると、スイッチの状態に対応した値が読み出されます。

以上で、AXIバスに追加したハードウェアが正しくLinuxのメモリ空間にマップされ、読み書きできることが確認できました。

Hello worldをやってみる

母艦PCでアプリケーションをビルドし、ssh経由でzynq上のubuntuに送り、実行してみます。
Xilinxの公式チュートリアルにあるようなやつのUbuntu(Linaro)版です。

SSHの設定

zynqのubuntuでSSHサービスを起動しておきます。

$ apt-get install ssh
$ /etc/init.d/ssh restart

さらに、作業用にユーザーを作ったり、SSHでのrootログインを禁止したりするとよいかもしれません。

プロジェクトの作成

母艦PCで作業します。
SDKで File -> New -> Application Project で新規プロジェクトを作成します。
OS Platformをlinuxにし、Nextを押した後の2ページ目でLinux Hello Worldを選びます。

プロジェクトが作成されたら、実行します。
上記で作ったプロジェクトを選択した状態で
Run -> Run Configurations
を選択します。
ダイアログでは、左ペインからRemote ARM Linux Applicationをダブルクリックします。
Remote ARM Linux Applicationが見つからない場合、左ペイン上部のメニューでFilter Configuration Typesなどにチェックが入っているかもしれません。チェックを外してみます。)

ConnectionのNewをクリックします。
SSH Onlyを選んでNext, 次のページでZynqのIPアドレスを入れてFinish。

次にRemote Absolute File Path for C/C++ ApplicationでBrowseを押します。
最初はssh接続のための設定ダイアログが出るので、適宜設定します。
ここでZynqのファイルシステムが見えます。
適当な場所にで右クリック -> New -> Fileでファイルを作り、OKします。
今回はMy Home/apps/hello_world_linux.elfとしました。

以上で設定は完了です。
Applyし、Runします。

エラー…

-bash: ./hello_world_linux: No such file or directory

とか出てうまく行きません。

http://zedboard.com/content/cant-start-my-binary-under-linaro
のとおり、rootで

$ cd /lib
$ ln -s arm-linux-gnueabihf/ld-linux-armhf.so.3 ld-linux.so.3

とします。

これで、

Hello World

ただしく表示されました!

Connect to xxx.xxx.xxx.xxx was canceled

SDKから実行しようとすると、
Connect to xxx.xxx.xxx.xxx was canceled(xxx.xxx.xxx.xxxはzynqのIPアドレス)と出て実行できないことがあります。
実行ファイルはZynqの上記で設定したパスにコピーされているので、
直接SSHして実行すればOKです。

これはSSHのバグ?なんですかね
http://stackoverflow.com/questions/33408763/cross-debugging-rseg1058-connect-to-192-168-xxx-xxx-cancelled