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のみのデザインを作成するだけです。特別なことはありません。
一応手順は書いておきますが、本当に普通です。
-
Create Block Design. Design nameは
system
とした。 -
Add IP -> ZYNQ7 Processing System
-
追加されたZynqコアをダブルクリックして設定ダイアログを表示
-
PresetsからZed boardを選択、OK
-
Run Block Automationを実行
-
FCLK_CLK0とM_AXI_GP0_ACLKを接続
-
Tool -> Validate Designを実行し、エラーが無いことを確認
-
Sourcesペインでsystemを右クリック -> Generate Output Products
-
Sourcesペインでsystemを右クリック -> Create HDL Wrapper
-
Generate Bitstream
必要なソースのダウンロード
ここから一旦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を立ち上げるので、そのための設定のようです。
-
include/configs/zynq-common.h
からCONFIG_EXTRA_ENV_SETTINGS
の定義(複数行の長い#define)をコピーしてinclude/configs/zynq_zed.h
の末尾に貼り付け。 -
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}; " \
-
#define CONFIG_EXTRA_ENV_SETTINGS
の前に#ifdef CONFIG_EXTRA_ENV_SETTINGS #undef CONFIG_EXTRA_ENV_SETTINGS #endif
を追記
- (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作成
- ツリービューで
zynq_fsbl_0
を選択した状態で、メニューからXilinx Tools -> Create Zynq Boot Imageを選びます。 - Output BIF file pathを適宜設定します。今回はファイル名を
ubuntu.bif
に変えました。 - ダイアログ下部のBoot image partitionsにはすでにbootloaderとビットストリームが読み込まれているはずです。無ければ、適宜Addします。
- 上で作ったu-bootを
u-boot.elf
とリネームして適当な場所にコピーしておきます。Boot image partitionsにAddします。Partition typeはdatafileです。 - 適宜Output pathを設定します。
- Create Imageを押します。Output pathで指定した場所に
BOOT.bin
ができます。
Device tree作成
まず、Device tree generatorをSDKに追加します。
- Xilinx Tools -> Repositories を選択
- Local RepositoriesでNewを押し、上でダウンロードした
device-tree-xlnx
ディレクトリを選択し追加 - OKを押してダイアログを抜ける。
次に、Device tree BSPを作成します。
- File -> New -> Board Support Package を選択
- Board Support Package OSの欄で
device_tree
を選択 - Finish
- 次に開く設定ダイアログで、bootargs行のValue列に
console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=1
と入力 - 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デザイン
こんな感じで、AXI GPIOを2つ追加し、それぞれLEDとスイッチにつながります。
アドレスマッピングはこんな感じ。
ハードウェアは以上。ビットストリームを作成し、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.bin
とdevicetree.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