- 1回目: とりあえずサンプルを動かす <--- 今回の内容
- 2回目: 通信方法やメモリマップについて
- 3回目: How to Debug Linux Application
- 4回目: How to Debug Baremetal Firmware
本記事について
Linux + ベアメタル(or FreeRTOS)のマルチ環境を、OpenAMPを使用してZYBOで実現させます。
- CPU0: Linux
- CPU1: ベアメタル (or FreeRTOS)
基本的には、ug1186-zynq-openamp-gsg_2018.pdfの内容と同じです。ただし、このドキュメントはZCU102ボードをターゲットにしていることと、あらかじめBSPが用意されている前提で書かれています。本記事では、ZYBOを対象に、作業内容が一から(ハードウェア作成から)分かるように記載しました。
まずは、サンプルプログラムである、echo_test
を動かせるようにします。
環境
- 開発用PC: Windows 10 64-bit
- Vivado 2017.4 WebPACKライセンス
- Xilinx SDK 2017.4
- 開発用PC (Linux): Ubuntu 16.04 本家 (日本語版じゃない) (on VirtualBox 5.2.4)
- PetaLinux 2017.4
- ターゲットボード: ZYBO (Z7-20)
手順は一から記載します。ただし、VivadoとPetaLinuxの基本的な説明は省略します。必要に応じて、こちらの記事を参照してください。
事前知識
まず最初に事前知識に説明です。具体的なやり方を先に知りたい方は、ここに飛んでください。
マルチプロセッサのコンフィギュレーション (AMP/SMP)
ZYBOにはCortex-A9のコアが2つ搭載されています(CPU0, CPU1)。
なお、本記事内でCPU, コア, プロセッサといった単語は全て同じ意味とします(本来は区別すべきですが、元ドキュメントや図に合わせました)。
AMP (ベアメタル/FreeRTOS)
XSDKで、ベアメタル(OS無し)またはFreeRTOSとしてプロジェクトを作成した場合は、シングルコア動作となります。CPU0とCPU1のどちらで動かすかはプロジェクト作成時に指定できます。また、XSDK上で2つプロジェクトを作り、FSBLやローダの設定をちゃんとすることで、CPU0, CPU1でそれぞれ別々のファームウェアを動かすことが出来ます。この場合は、AMP(Asymmetrical multi processing)動作となり、各ファームウェアは決められたCPUでのみ動作します。このとき、L1キャッシュのコヒーレンシも保たれません。ちなみに、L2キャッシュがどうなるかは不明。
SMP (Linux)
PetaLinuxで、通常通りにLinuxイメージを作成した場合は、SMP(Symmetrical multi processing)動作となります。通常のPCと同じ動きになります。Linux上の各プロセスは、CPUの空き状況や優先度に応じて、CPU0,CPU1に動的にスケジューリングされます。同じプロセスでも、ある時はCPU0で動き、ある時はCPU1で動くというようになります。このとき、動作するCPUが変わってもL1キャッシュのコヒーレンシは保たれます。
AMP (Linuxとベアメタル(or FreeRTOS))
開発するプロダクトの性質に応じて、Linuxとベアメタルを動かしたいというケースが出てきます。例えば、以下のような処理が必要な場合が考えられます。
- Linux: USBやネットワーク周りの処理。UI関係の処理
- ベアメタル: リアルタイム性の高いデバイス制御
このとき、例えば、CPU0でLinuxを動かし、CPU1でベアメタルを動かしたい、となります。また通常は、各CPUで動作する処理は協調して動く必要があります。そのため、CPU間通信機能も必要になります。
これらを実現するために、OpenAMP を使用します。
その他
巷には、Linux上の一つのプロセスとしてベアメタルプログラムやRTOSタスクを走らせたり、逆にRTOS上でLinuxを動かすといった方法もあります。
また、ハイパーバイザを使うという方法もあるそうです。
OpenAMP
OpenAMPとは、The Multicore Associationによって管理されている、マルチプロセッサ用のオープンなフレームワークです。
OpenAMPによって、以下のことが可能になります(他にも色々あると思うが、今回使うものだけ)。
- Linuxからベアメタル(or FreeRTOS)用ファームウェア(elfバイナリ)のロードを行う
- 通信機能の提供
重要な点として、OpenAMPを使うときは、ベースとなるOSはLinuxになります。まずCPU0(ホストプロセッサ)でLinuxを起動して、CPU1(リモートプロセッサ)にベアメタル側のファームウェア(リモートアプリケーション)をロードします。
OpenAMP コンポーネント
OpenAMPは下記3つのコンポーネントを持ちます。
- virtIO
- 共有メモリの管理
- remoteproc
- リモートプロセッサのライフサイクル管理。ファームウェアをロードしたりする
- RPMsg
- 異なるコアで動作するプロセス間の通信機能(IPC: Inter-Process Communication)
Libmetalライブラリ
LibmetalはOpenAMPによって提供されるライブラリです。LibmetalはLinux, FreeRTOS, ベアメタル環境で使用可能です。
Libmetalライブラリが、デバイスアクセス、割り込み、メモリアクセスといったハードウェアを抽象化したAPIを提供します。
OpenAMPの各コンポーネントがLibmetalライブラリを使用します。
プロセッサ間のメッセージ通信の仕組み
基本的なプロセッサ間通信の流れとしては、以下のような感じです。
- 送信側プロセッサが、共有メモリ(Shared Memory)にデータを書く
- 送信側プロセッサが、プロセッサ間割り込み(IPI: Inter Processor Interrupt)を発生させ、受信側プロセッサに通知する
OpenAMPを使用したAMP環境の構築
ここから、実際にLinux+ベアメタルのAMP環境を作っていきます。作業の流れは以下のようになります。
- ハードウェアの作成 (Vivado)
- 特別なことは不要
- ベアメタル (or FreeRTOS) 用ファームウェアの作成 (XSDK)
- Linuxイメージの作成 (PetaLinux)
- OpenAMPを有効にする
- 2で作成したファームウェアをインストールする
- デバイスツリーの編集
ハードウェアの用意 (on Vivado)
まずVivadoで、PSだけを配置したハードウェアを作ります。デザインの名前はデフォルトのままで、design_1
にします。また、必要に応じてEthernet 0の設定を修正しておいてください。こちらを参照。
hdfをエクスポートして(ビットストリーム込みで)、Ubuntu側にコピーしておきます。コピー先は~/work/peta/design_1_wrapper.hdf
とします。
また、次の作業のために、メニューバー -> File -> Launch SDKで、XSDKを起動します。
ベアメタルファームウェアの作成 (on XSDK)
echo_test用サンプルプロジェクトの作成
メニューバー -> File -> New -> Application Projectで、新しいプロジェクトを作成します。重要な設定は、CPU1(ps7_cortexa9_1)で動作させる点です。
- Project Name: my_echo_test (何でもいい)
- OS Platform: standalone
- Hardware Platform: design_1_wrapper_hw_platform_0 (Vivadoで作成したもの)
- Processor: ps7_cortexa9_1 ⇐ 重要
- Language: C
- Templates: OpenAMP echo-test
bspをカスタマイズする
Project Explorer -> my_echo_test_bspで右クリック -> Board Support Package Settingsをクリック。
ps7_cortexa9_1を選択して、extra_compiler_flagsのValueに -DUSE_AMP=1
を追加します。
また、openampを選択して、WITH_PROXYをfalseに設定します。これは、Remote Procedure Call (RPC)機能を使う時にはtrueにする必要があります。今回は使用しないので、falseにします。(が、別にデフォルトのままでも大丈夫なようです。コードサイズが増えるだけっぽい)
ファームウェアの作成
ソースコードは何も変えずに、ビルドします。(デフォルトでオートビルドになっている)
作成されたmy_echo_test.elf
をUbuntu側にコピーしておきます。
Linuxイメージを作る (on PetaLinux in Ubuntu)
PetaLinuxプロジェクトを作る
下記コマンドで、PjOpenAmpというプロジェクトを作成します。そして、先ほどVivadoで作成したハードウェアで設定をします。コンフィグ設定画面では何もせずにExitします。
cd ~/work/peta
petalinux-create --type project --template zynq --name PjOpenAmp
cd PjOpenAmp
petalinux-config --get-hw-description=~/work/peta
Kernelコンフィグ
Kernelのコンフィギュレーションをします。ただ、これはデフォルト設定でOKなはずなので、確認だけ行います。
petalinux-config -c kernel
下記設定が有効になっていることを確認してください。
- Enable loadable module supportが、
[*]
- Device Drivers > Generic Driver Options > Userspace firmware loading supportが、
-*-
- Device Drivers > Remoteproc drivers > Support ZYNQ remoteprocが、
<M>
rootfsコンフィグ
rootfsのコンフィギュレーションをします。
petalinux-config -c rootfs
下記のように設定してください。libmetalとOpenAMPを有効にします。また、テスト用にテストアプリをインストールします(これはなくてもOK)。もしかしたら、packagegroup-petalinux-openampだけで大丈夫かもしれません。
- Filesystem Packages > misc > sysfsutils > libsysfsを、
[*]
- Filesystem Packages > libs > libmetal > libmetalを、
[*]
- Filesystem Packages > misc > packagegroup-petalinux-openamp > packagegroup-petalinux-openampを、
[*]
- Filesystem Packages > misc > openamp-fw-echo-testd > openamp-fw-echo-testdを、
[*]
- Filesystem Packages > misc > openamp-fw-mat-muld > openamp-fw-mat-muldを、
[*]
- Filesystem Packages > misc > openamp-fw-rpc-demo > openamp-fw-rpc-demoを、
[*]
リモートアプリケーション(ベアメタルファームウェア)を含める
先ほどXSDKで作成したベアメタルファームウェアである、my_echo_test.elfを取り込みます。
まず、MyRemoteAppEchoTestという名前の、ファイル取り込みだけをするアプリケーションを作成します。そこに、バイナリファイルをコピーします。最後に、レシピ(MyRemoteAppEchoTest.bb)を編集します。
これによって、my_echo_test.elfが/lib/firmware/my_echo_test.elf
にインストールされます。
petalinux-create -t apps --template install -n MyRemoteAppEchoTest --enable
cp ~/work/peta/my_echo_test.elf project-spec/meta-user/recipes-apps/MyRemoteAppEchoTest/files/.
code project-spec/meta-user/recipes-apps/MyRemoteAppEchoTest/MyRemoteAppEchoTest.bb &
SUMMARY = "Simple MyRemoteAppEchoTest application"
SECTION = "PETALINUX/apps"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://my_echo_test.elf \
"
S = "${WORKDIR}"
INSANE_SKIP_${PN} = "arch"
do_install() {
install -d ${D}/lib/firmware
install -m 0644 ${S}/my_echo_test.elf ${D}/lib/firmware/my_echo_test.elf
}
FILES_${PN} = "/lib/firmware/my_echo_test.elf"
デバイスツリーを編集する
割り込み番号や、リモートアプリケーション用メモリ領域の設定のため、デバイスツリーを編集します。これは、ug1186-zynq-openamp-gsg_2018.pdfに記載されている内容そのままです。
code project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi &
/include/ "system-conf.dtsi"
/ {
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
rproc_0_reserved: rproc@3e000000 {
no-map;
reg = <0x3e000000 0x01000000>;
};
};
amba {
elf_ddr_0: ddr@0 {
compatible = "mmio-sram";
reg = <0x3e000000 0x400000>;
};
};
remoteproc0: remoteproc@0 {
compatible = "xlnx,zynq_remoteproc";
firmware = "firmware";
vring0 = <15>;
vring1 = <14>;
srams = <&elf_ddr_0>;
};
};
ビルドしてイメージを完成させる
petalinux-build
petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/design_1_wrapper.bit --u-boot
上記コマンドで生成された、images/linux/BOOT.BIN
とimages/linux/image.ub
を、SDカード(FAT32)にコピーします。
自分で作成したエコーテストを試す
先ほど作成したSDカードをZYBOにさして、ZYBOのJP5をSDカード起動に設定します。そして、電源ONします。
UARTターミナルから、以下コマンドを実行します。
modprobe rpmsg_user_dev_driver
echo my_echo_test.elf > /sys/class/remoteproc/remoteproc0/firmware
echo start > /sys/class/remoteproc/remoteproc0/state
echo_test
すると、このように動いていることが分かります。
root@PjOpenAmp:~# echo_test
Echo test start
~省略~
1 - Send data to remote core, retrieve the echo and validate its integrity ..
2 - Quit this application ..
CMD>1
sending payload number 0 of size 9
echo test: sent : 9
received payload number 0 of size 9
~省略~
echo test: sent : 478
received payload number 470 of size 478
sending payload number 471 of size 479
echo test: sent : 479
received payload number 471 of size 479
**************************************
Test Results: Error count = 0
Linux側のアプリケーションとベアメタル側のファームウェアが通信しています。
確認できたらファームウェアを停止します。これによって、OpenAMP動作も停止します。
echo stop > /sys/class/remoteproc/remoteproc0/state
備考
おそらく不要ですが、場合によっては modprobe zynq_remoteproc
が最初に必要かもしれません。
OpenAMPのメッセージ通信用のモジュールをアンロードする場合は、 modprobe -r rpmsg_user_dev_driver
を実行してください。
AMPになっていることを確認してみる
起動直後に、/proc/cpuinfoを見ると、CPU0とCPU1があります。しかし、echo start > /sys/class/remoteproc/remoteproc0/state
後に見ると、CPU0しかないことが分かります。
root@PjOpenAmp:~# cat /proc/cpuinfo
processor : 0
model name : ARMv7 Processor rev 0 (v7l)
-省略-
processor : 1
model name : ARMv7 Processor rev 0 (v7l)
-省略-
Hardware : Xilinx Zynq Platform
-///////////////////////////////////-
-echo start > /sys/class/remoteproc/remoteproc0/state 実行後-
-///////////////////////////////////-
root@PjOpenAmp:~# cat /proc/cpuinfo
processor : 0
-省略-
Hardware : Xilinx Zynq Platform
デモアプリを試す
順番が逆になってしまいましたが、自分でファームウェアを作らないでも、デモアプリを使ってOpenAMPを試すことができます。それを実行してみます。
rootfsでopenamp-fw-XXXを有効にしたことで、以下のバイナリ(ファームウェア)が/lib/firmwareにインストールされています。ちなみに、自作のmy_echo_test.elfも同じ場所にインストールされています。
- /lib/firmware/image_echo_test
- /lib/firmware/image_matrix_multiply
- /lib/firmware/image_rpc_demo
image_echo_testを試す
image_echo_testはmy_echo_test.elfと同じ動作をします。自作のmy_echo_test.elfがうまく動かない場合は、image_echo_testを試すことで、問題の切り分けが出来るかもしれません。
modprobe rpmsg_user_dev_driver
echo image_echo_test > /sys/class/remoteproc/remoteproc0/firmware
echo start > /sys/class/remoteproc/remoteproc0/state
echo_test
echo stop > /sys/class/remoteproc/remoteproc0/state
image_matrix_multiplyを試す
modprobe rpmsg_user_dev_driver
echo image_matrix_multiply > /sys/class/remoteproc/remoteproc0/firmware
echo start > /sys/class/remoteproc/remoteproc0/state
mat_mul_demo
echo stop > /sys/class/remoteproc/remoteproc0/state
image_rpc_demoを試す
proxy_app
を実行するだけで、ファームウェアのロードなどを行ってくれるようです。そのため、事前に他のファームウェアをロードしていた場合には、終了させておく必要があります。
echo stop > /sys/class/remoteproc/remoteproc0/state
modprobe -r rpmsg_user_dev_driver
proxy_app
おわりに
ひとまずコードの中身や仕組みは置いといて、手順としてOpenAMP環境を作れるようにしました。次回は実際にコードをいじってみようと思います。
参考
- ug1186-zynq-openamp-gsg_2018.pdf
- http://www.wiki.xilinx.com/Multi-OS+Support+%28AMP+%26+Hypervisor%29
そういえば
msysやmingwのbinにパスが通っていると、XSDKでOpenAMPプロジェクトを作成したときにフリーズする可能性があるのでご注意ください。