1.記事一覧
記事は複数回に分けて投稿する予定です。AMD/XilinxのARM SoC(ZynqMP Kria K26 SOM)で動作する、AMD/Xilinx公式認定Ubuntuやベアメタル開発を勉強していく覚書きです。
ZynqMP(APU:Cortex-A53,Arm64) 組込みLinux入門編
ZynqMP(RPU:Cortex-R5,Arm32) RPU入門編
- 【ZynqMP】1. Cortex-R5でHelloworld ←本記事
- 【ZynqMP】2. Cortex-R5でRTOS+GPIO
2.RPU(Real-time Processing Unit)に触ってみる
ZynqMPにはLinuxが動かせる、ARM Cortex-A53 MPCore(以後CA53)が4コア搭載されていて、APU(Application Processing Unit)と呼ばれています。そして、他にCortex-R5F(CR5)が2コア搭載されて、こちらはRPU(Real-time Processing Unit)と呼ばれています。
出典<https://japan.xilinx.com/products/silicon-devices/soc/zynq-ultrascale-mpsoc.html>
Cortex-Rプロセッサはその名の通りリアルタイム処理を目的とした32bitコアになります。どちらかといえばCortex-M系よりCortex-A系に近い構造です。
マイクロコントローラのCortex-M系より、比較的高クロックでL1キャッシュ・AXIバス搭載とリッチな構成になっています。L1キャッシュを搭載していると言うことは、キャッシュヒット率による応答時間の(最悪値の)ばらつきの懸念がでてきます。その場合のために、アクセスウェイトがほとんどかからないTCM(密結合メモリ)を搭載しています。
CR5プロセッサは2コアで1コア分の動作をする、デュアルロックステップ機構を持っています。これはハードの冗長化を行い、故障に対する信頼性を向上させるものです。いわゆる機能安全規格向けの機能になります。自動車のECUに搭載する場合には必要な要件になります。必要ない場合は2コア別で動かすことも出来ます。
Cortex-Rが多く使われている場所は車載向けSoCのCAN通信制御やコンピュータのストレージコントローラ(HDDやSSDの制御chip)に多いとよく聞きます。
最後に動作クロックですが、KR260に搭載されるK26 SOMの場合、533MHzで動作するようです。ZynqMPはスピードグレードによっては最高600MHzです。
2-1. CR5(Cortex-R5)でHello World
長々と紹介してみましたが今回は全然大したことはやりません。シリアルへHello worldを出力するのをやってみます。テンプレートにHelloworldのサンプルがあるのでこちらを使ってみましょう。
3.環境
3-1. 新Vitis Unified IDE(2023.2)を使ってみる
2023.2で、Vitis IDEが新しくなっていることを知りました。Vitis AIやVitis HLSが統合されたメジャーアップデートになるようです。
試しに起動してみるとなんとEclipseベースだった環境がVisual Studio Code(VSCode)ベースになっていました。(ちなみに旧Vitisも入ってます。shell$ vitis -classic & で起動できます)
【追記】Eclipse Theia IDE をベースにしているとのことです。(Visual Studio Codeが元にはなっているようですが)
旧Vitis(Vitis Classic)はUIが今一つだったので、テーマをいろいろいじっては悪化して戻らなくなるという経験をしました。暗色背景に暗色文字(その逆もあり)が見づらく、結局直せませんでした(笑)。VSCodeへの移行は膨大な工数がかかったと予想できますが、AMDさん、Xilinxさんナイスな判断だと思います(日本企業だとおそらく出来ない)。
ところでVSCodeはMicrosoftさんの製品ではあるのですが、2015年?あたりにオープンソース化(github)されてMITライセンスになっています。世の中の組込み向けマイコンベンダの多くは開発環境がEclipseベースですが、いずれVSCodeに移行する流れがくるのでしょうか。
もう一つ驚きなのが、ビルドシステムの刷新です。詳しくはこちらのXilinx-Wikiにあります。ざっくり違いを説明すると、
- 従来:.xsaからソフトウェア構成にmdd,mld,mssファイルを使用し,Xilinxツール依存大
- 今回:.xsaからSystem-Device-Tree,lopper(Python),CMake,YAMLを使用し、Xilinxツール依存小
と、ほぼ全面的に変わっているようです(これも相当な工数がかかったと思われます)。Xilinxツール(XSCT,HSIなど)に依存度が高い現状をオープンソースツールを活用して改善しようという試みらしいです。オープンソース文化が強いAMDさんが後押ししたのでしょうか。
System-Device-Tree(SDT)とはLinuxのdevicetreeのスーパーセットに当たるもので内容が拡張されています。これをハードウェアメタデータの代わりとするようです。lopperはdevicetree.orgが提供するPythonライブラリでdevicetreeのパースを行うことが出来るようです。
SDT導入に関してベアメタルアプリの開発の気になる点がありました。
- ベアメタルドライバの互換性が一部なし
- timerライブラリ(xiltimer)の組込みが必須になる
xparameters.hからDeviceIDを調べてドライバの初期化関数を使用していましたが、これがデバイスのベースアドレスに変わるようです。この部分の関数の互換性がありません。devicetree自体がデバイスの区別をベースアドレスで行うことからこの仕様になったと思われます。
以下にAXI-GPIOのベアメタルドライバのサンプルコードの抜粋を貼っておきます。
//AXI GPIOベアメタルドライバの例
#ifndef SDT
Status = XGpio_Initialize(InstancePtr, DeviceId);
#else
Status = XGpio_Initialize(InstancePtr, BaseAddress);
#endif
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
また、DeviceIDがなくなったことと、SDTに親割込みコントローラの情報を持つことによって、割込みの初期化周りの関数の扱いも変わるようです。こちらはAXI-GPIOのドライバのサンプルを見る限りは初期化処理がよりシンプル…というか1関数一発で設定完了できているようです。
次にxiltimerライブラリの使用が必須の件ですが、スリープや周期タイマの機能をハードウェアタイマが提供します。K26 SOMの場合はTTCタイマを使用します。APU側で認定Ubuntuを動かす場合、FAN制御のPWM出力用にTTC0を使っていますので、OpenAMPなどを動かす場合気をつける必要があります。
TTC(トリプルタイマカウンタ)は文字通りカウンタを3つ内蔵するタイマで、それがTTC0、TTC1、TTC2、TTC3の4つあります。
もし仮にFreeRTOSでTTC0を選択した場合、一見うまく動いているかのような挙動を示します。UbuntuのFanサービスではカウンタ3でPWMを使用し、Vitisではデフォルトでカウンタ1を選択するので、カウンタの競合は無いからです。私も仕様をすべて把握しているわけではありませんが、割込みまわり、PM系(Power Management)、Clockで独立しているとは限らないので、別にしておく方が良いかなと思います。
【追記】新Vitisv2024.1ではFreeRTOSのデフォルトがTTC0以外に設定されていました。
3-2. 前提条件
CR5でHelloworldするにあたっての前提条件をまとめます。
開発PC | Ubuntu20.04 LTS |
開発ツール | ADM/Xilinx社 Vivado・Vitis Unified IDE v2023.2.1 |
ターゲットボード | Xilinx社製 Kria KR260 ロボティクススターターキット(SK-KR260-G) SOM : Xilinx社 Kria K26 SOM (Zynq UltraScale+ MPSoCベース) APU : Arm CA53x4 1333MHz arm64 RPU : Arm CR5x2 533MHz armv7-R SDRAM : DDR4 4GB |
その他 | MicroUSBケーブル1本(JTAG, USB-UART用) |
4.VivadoでPLを作成
注意:Vivadoの詳しい操作方法は説明しません。要点だけ載せます。
4-1. プロジェクト作成
Note:プロジェクトを作成するときの注意点です。Project Typeでは
“Project is an extensible Vitis platform”にチェックを入れません。
理由はチェックを入れるとVitisを起動した際、以下のようにアプリケーションプロジェクトでデバッグ機能が働かないからです。理由はわかりません。
本来は以下の内容が出てほしいです。
この現象はかなり困ったのですが、Whitney Knitter氏のGetting Started with the Kria KD240 in Vitis Unified 2023.2の記事で対策方法を見つけることが出来ました。先人に感謝。
4-2. IPブロック作成
IPブロックは今回は何も接続しません。
ビットストリームを生成できたらbitstreamを含む設定でエクスポートします。
5.Vitis Unified IDEでプロジェクト作成
Vitis Unified IDEを起動します。Vivadoのツールタブで起動する方法の他に、Linuxならshellで起動できます。旧Vitis(Vitis classic)も入っているので不具合が出たときにこちらを選ぶことも出来ます。
起動できたらOpen Workspaceで作業フォルダを指定します。
#おまじない
$ source /tools/Xilinx/Vitis/2023.2/settings64.sh
# Vitis Unified IDE
$ vitis &
# 旧Vitisも起動可能
$ vitis -classic &
5-1. Platformコンポーネントを作成
今までもそうでしたが、Vitisで組込み開発する場合はplatformプロジェクトをベースにapplicationプロジェクトを作成する2段構成になります。platform側はBSPを構成するためのプロジェクトになります。ただし、Vitis 2023.2からはプロジェクトではなくコンポーネントと呼ばれるようになっています。
左上のFile>New Component>Pratformを選択します。
pratform名を決めます。
vivadoからエクスポートした.xsaを選択します。
次に行くと.xsaを解析するのに少し時間がかかります。構成が自動設定されます。ProcessorをCR5の0番コアに設定します。
- Operating system: standalone
- Processor: psu_cortexr5_0
デフォルトでGrenerate Boot artifactsにチェックが付きますが、これはFSBLを自動生成します。FSBLの実行コアはCA53がデフォルトになりますが、このままで行きます。
次にサマリが表示され、Finishでコンポーネント生成が始まります。生成はそこそこ時間がかかります。
5-2. Platformコンポーネント(BSP)の設定の確認
BSPの設定周りを見ていきます。左のファイルツリーからPlatform>Settings>vitis-comp.jsonを選択します。
まずはライブラリ周りからです。xiltimerがデフォルトで有効になっています。先程も記述しましたが、ベアメタルドライバでxiltimerは必須項目になっているためです。
コンパイラ周りの設定です。
標準出力stdoutと標準入力stdinのポート設定です。psu_uart_1はPSのuart1で、KR260ではJTAG用のUSBケーブルを経由する、USBシリアルとして使えます。
ちなみに、VivadoでAXI-UartLite IPをPLに実装してあると、ここに選択肢として選ぶことが出来るようになります。uart1はLinux側でシリアルコンソールとして使用するので、これを無効化したくない場合は、AXI-UartLite IPでRX,TX線を外部出力し、USBシリアル(FTDIなど)チップで接続するといった方法が使えます。
xiltimerの設定です。PSのTTCタイマを利用するので、使用するタイマがバッティングする場合は変更しておきます。認定UbuntuではFAN制御にTTC0を使うのでLinuxを同時に動作させる場合は注意が必要です。
driversは使用しているverを確認できます。
今回はBSPはデフォルトのままの設定で使用します。platformをビルドします。FlowウィンドウののBuildボタンを押します。
5-3. Applicationコンポーネントの作成
Applicationコンポーネントはテンプレートから作成することにします。左側のバーでExamplesを押し、Hello Worldを押して、Create Application Component from Templateを押します。
Application名を入力します。
先程作成したPlarformを選択します。
ドメイン選択です。デフォルトのままで次に行きます。
最後にサマリを確認してFinishボタンを押して完了です。
5-4. Applicationコンポーネント設定の確認
vitis-comp.jsonを見てみます。特段設定するところは無さそうです。Navigate to BSP SettingsはPlatromのvitis-comp.jsonへのリンクです。
続いてCMakeLists.txtです。
UserConfig.cmakeはUIで設定できるようになっています。四角で囲った部分のボタンでコードを表示もできます。
最後にlaunch.jsonです。デバッグ用設定になります。UIモードとjsonモードがあります。
デバッグ設定で注意しないとならないのが、デバッグ実行時のPLへダウンロードするビットストリームファイルはApplicationコンポーネントフォルダ内にPlatformコンポーネントからコピーしたものを参照しているようです。
つまり、Vivadoでハードウェアファイル(.xsa)を更新したときにPlatform側でvitis-comp.jsonの”switch xsaボタン”による更新(←そもそもこれが正規手順なのか?)をした場合でも、デバッグ時は古いビットストリームファイルのままになります。とりあえず下記のパスをPlatform側に変えるか、手動でビットストリームファイルをコピーする対策をします。
6.Hello worldをJTAGデバッグする
作成されたHello worldのソースを見てみます。シンプルですね。
Applicationコンポーネントをビルドします。旧VitisではPlatformコンポーネントに変更が合った場合自動で逆上ってリビルドを掛けてくれますが、新Vitisではそれをやってくれないみたいなので、手動でビルドし直します。
【追記】新Vitis v2024.1からPlatformもApplicationもまとめてビルドし直されるようになりました。ただし、たまにビルドできていないことがあるので注意です。
6-1. KR260のbootmodeをJTAGbootに切り替える
Vitisでデバッグ開始する前に、ZynqMPはboot設定端子でbootmodeをJTAGbootに設定しますが、KR260キャリアボードには設定端子がありません。基本的にK26 SOMはQSPI ROMからFSBL、u-bootを起動しますので、QSPI boot固定のようです。
新Vitisではデバッグ実行するとJTAGbootモードに強制的に移行しますか?とダイアログが出て、そのままJTAGダウンロードを開始できます。しかし、JTAGを再接続したりすると、DAP周りでのエラーが出ます。これはボードの電源を入れ直すと解消できました。
しかし、開発体験が悪いのでいい方法がないか調べてみると、公式Docでは、JTAG経由でTCLを実行して、bootmodeレジスタを上書きする、ことで対応出来るようです。これを試すと、何度かJTAGを接続し直してもDAPエラーが出ることはなくなりました。
手順は以下のとおりです。
- SDカードを外す(Linuxイメージが入っている場合破損の危険があるので)。
- JTAG起動用のTCLファイル作成
- JTAG/シリアル用USBケーブル接続とボード電源のON
- XSCTでTCLを実行
- Vitisによるデバッグ
- ボードの電源をOFFにすると元のQSPI bootmodeに戻る
順にやっていきます。
1.SDカードを外します。
2.以下のTCLファイルを作ります。ファイル名はboot_jtag.tclにします。
proc boot_jtag { } {
############################
# Switch to JTAG boot mode #
############################
targets -set -filter {name =~ "PSU"}
# update multiboot to ZERO
mwr 0xffca0010 0x0
# change boot mode to JTAG
mwr 0xff5e0200 0x0100
# reset
rst -system
}
# Jtag接続
connect
puts stdout "Connect Jtag"
# JtagBoot切り替え
puts -nonewline stdout "Start transition to jtag boot mode..."
boot_jtag
puts stdout "done."
# Jtag切断
puts -nonewline stdout "Disconnect Jtag..."
disconnect
puts stdout "done."
3.ボードにJTAG/シリアル用のUSBケーブルを接続します。
ここで、Ubuntuならscreenコマンド(WinならTeraterm)でu-bootのメッセージを観察してうまく行くか様子を確認してみましょう。成功すればresetがかかるはずなので、メッセージが途中で止まるはずです。
ボードの電源を入れます。シリアルにu-bootのログが出てきます。
4.XSCTでTCLを実行します。
# おまじない
$ source /tools/Xilinx/Vitis/2023.2/settings64.sh
$ ls
boot_jtag.tcl
# TCl実行
$ xsct boot_jtag.tcl
実行結果です。
$ xsct boot_jtag.tcl attempting to launch hw_server ****** Xilinx hw_server v2023.2.1 **** Build date : Dec 8 2023 at 09:27:31 ** Copyright 1986-2022 Xilinx, Inc. All Rights Reserved. ** Copyright 2022-2023 Advanced Micro Devices, Inc. All Rights Reserved. INFO: hw_server application started INFO: Use Ctrl-C to exit hw_server application INFO: To connect to this hw_server instance use url: TCP:127.0.0.1:3121 Connect Jtag Start transition to jtag boot mode...done. Disconnect Jtag...done. $
u-bootログも途中で止まりました。resetが掛かったようです。
6-2. JTAGデバッグ実行
デバッグを実行します。launch.jsonのデバッグ設定はデフォルトのままで行きます。
デバッグ画面に切り替わります。元に戻りたいときは左端のバーで切り替えます。
CA53の0番コアとCR5の0番コアがブレーク状態になっています。これはPlatformコンポーネント作成時にFSBLの実行コアをCA53に設定したためです。CA53側がアクティブになっているため、右側の逆アセンブリはCA53がFSBLの処理が完了し、Handoff後のExitでブレークしている状態です。
CR5側をアクティブにします(上画像の緑枠を押す)。main関数先頭でブレークしています。
試しに実行してみます。Hello worldが無事出ました。
6-3. シリアルコンソールについて
旧Vitisにあったシリアルコンソールはなくなっていますが、開発PCにUbuntuなどのLinuxを使っている方であれはターミナルshellを内部に表示出来るので、screenなどが使えます。
【追記】新Vitis v2024.1でシリアルコンソールを使う方法はいくつかあります。PCがUbuntuの場合で話します。Windowsの方はTeratermで良いと思います。
- ・Vitisを使わず、Shellでcu、screenを使うか、GUIツール(私はCuteCom)を使う。
→一番確実な方法。(ただし、CoreSight(Armコア)やMDM(MicroBlaze)経由の場合のやり方はは私はまだ知りません) - ・VitisのSerial Monitorを使う(詳細はこの後で)。ただし、Vitis v2024.1でアーリーアクセス版の提供となり、受信したデータの表示は出来るが、送信はわからない(私は出来なかった)
- ・CoreSight(Armコア)やMDM(MicroBlaze)のデバッグIPを経由するシリアル通信はVitisのDEBUG CONSOLEかXSCTのjtagterminalコマンドを使用する(詳細はこの後で)
★新VitisのSerial Monitorを使うシリアル通信方法
注意:こちらはアーリーアクセス版の機能です(Vitis v2024.1)。今後機能拡張されるようです。
最初に機能を有効化する必要があります。上図の方法を参照。
やり方
左上Vitis>Serial Monitorでシリアルポート選択します。
通信速度選択します。
下のコーンソールゾーンに新タブが現れます。送信のやり方はわかりません。もしかして未実装で開発中かもです。
【追記】そのまま下記のカーソルをアクティブにした状態で、キーを入力するとそのまま送信されます。どうやら中身は”Web and Desktop Serial Monitor : eclipse-cdt”拡張機能がデフォルトで組み込まれていて、その機能が使えるようです。
★CoreSight(Armコア)やMDM(MicroBlaze)のデバッグIPを経由するシリアル通信方法
Platformコンポーネントののstdout、stdin設定を”psu_coresight”または”mdm”に設定すると、JTAGバス経由のシリアル通信(セミホスティング的なもの?)を使用でき、データ受信すると下部の”DEBUG CONSOLE”タブに表示されます。UARTを用意しなくて良くて便利ではあるのですが、、、、
上図の右側にあるように、デバッグセッションごとにセッションが新しく作られるので、手動で切替える必要があります。現状、私はデバッガのリスタートボタンがうまく使えない(何か設定あるんだろうか)ので、一度停止させてから再度デバッグスタートしています。これをやるとセッションが新しく作成されるので、いちいち切り替えないとメッセージ出力が見れません。だったら最初から普通のUART使うかなという印象です。
ちなみにこの機能を調べてみるとXSCTの”jtagterminal”コマンドの入出力をバイパスしているようです。こちらだとセッション切り替えのひと手間が不要です。
やり方はXSCTでJTAG hw_serverに接続します。
(Shell)$ xsct #xsctを対話モードで起動
xsct% connect #JTAG hw_serverに接続
tcfchan#0
xsct% targets #ターゲットCPU一覧表示
1 PS TAP
2 PMU
3 MicroBlaze PMU (Sleeping after reset. No clock)
4 PL
5 MicroBlaze Debug Module at USER2
6 MicroBlaze #0 (Running)
7 PSU
8 RPU
9 Cortex-R5 #0 (Step)
10 Cortex-R5 #1 (Suspended)
11 APU
12 Cortex-A53 #0 (Breakpoint, EL3(S)/A64)
13 Cortex-A53 #1 (Power On Reset)
14 Cortex-A53 #2 (Power On Reset)
15 Cortex-A53 #3 (Power On Reset)
xsct% targets 9 # Cortex-R5 #0コアをターゲットに設定
xsct% jtagterminal # 別ウィンドウでシリアルターミナル起動
xsct% jtagterminal -stop # ターミナル停止
xsct% disconnect
xsct% exit
(Shell)$
シリアルターミナルは下の様な見た目です。デバッグ開始→停止→デバッグ開始→停止とやって、2回分のサンプルメッセージが出力出来ています。(使い勝手は…これからに期待!)
【注意】CoreSight(Armコア)やMDM(MicroBlaze)で標準出力を使用する際に気をつける点
これらのデバックIPを経由するstdioはJTAGbootなどで使用します。リリース用にROM化して起動しようとすると、JTAGが接続されていないのでCPUの処理がブロッキングして止まることが有ります。私はstdout,stdinをNoneに戻すのを忘れてよく止まってましたね…