組込み

【ZynqMP】5.1.動作中のサブコアにJTAGを接続する

1.記事一覧

記事は複数回に分けて投稿する予定です。AMD/XilinxのARM SoC(ZynqMP Kria K26 SOM)で動作する、AMD/Xilinx公式認定Ubuntuやベアメタル開発を勉強していく覚書きです。

Zynq7000(armv7)版はこちら

2.コア間通信の疎通確認時に…サブコアが応答なし

今回は以前私がやっていた「UARTでAMP【コア間通信】」を開発する際の、デバッグのやり方に関する話です。Linux(CA53)とサブコア(CR5・Microblaze)でコア間通信のファームを作る際は、Linux側、サブコア側で個別にデバッグを行ってから結合デバッグを行うと思います。

サブコア単体では動作するのに、いざLinux側からサブコアのファームをロードして動作させると、動かなくなった…さあどうする?というのが今回のテーマです。

UARTのコア間通信では、サブコアのELFロードをOpenAMPのRemoteprocドライバを使って実現していました。一方個別のサブコアデバッグではVitisによるJTAGロードです。そのままの設定では、ローダまわりがバッティングするので、Remoteprocから起動したサブコアをデバッグすることは出来ません。

ただし、設定を変えればデバッグは可能です。本記事では起動中のサブコアにJTAGデバッガを接続する方法をやってみます。2つのやり方でやってみます。

  • Shell(XSCT)から動作中のサブコアに接続
  • Vitisから動作中のサブコアに接続

環境

開発PCUbuntu20.04 LTS
開発ツールAMD/Xilinx社
Vivado v2024.1.1
Vitis Unified IDE v2024.1.1
ターゲットOSXilinx認定Ubuntu22.04
Linux Kernel : 5.15.0-1031-xilinx-zynqmp
ターゲットボード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

3.下準備

ZynqMPでUbuntuなどのOSを動作させているときに、XSCTなどでJTAG接続を行うと、CA53がハングしてOSがフリーズします。

この問題は以下のサポートで回避方法が示されています。

69143 – Zynq UltraScale+ MPSoC: Connecting XSDB to Linux CPU idle

Ubuntuのbootargsに”cpuidle.off=1″を追加します。CA53がアイドル状態で低電力状態に遷移していることが原因のようです。デバッグが終わったら戻します。

bootargsの書換えは以前の記事「【ZynqMP】3.認定UbuntuのDeviceTree変更」で紹介しています。

簡単な説明としては”/etc/default/flash-kernel”のLINUX_KERNEL_CMDLINEを書き換えて、flash更新コマンドを実行するだけです。

Bash
#bootargs
$ cat /etc/default/flash-kernel
LINUX_KERNEL_CMDLINE="cpuidle.off=1"
LINUX_KERNEL_CMDLINE_DEFAULTS=""

#flash更新
$ sudo flash-kernel

4.Vitisから動作中のサブコアに接続

4-1. Shell(XSCT)から動作中のサブコアに接続

XSCTによるコンソールコマンドでデバッグを行う方法です。GDBをコマンドでポチポチするような感じで操作します。Vitisでも出来るのですが、使いにくいのでメインはこちらになると思います。

やり方やコマンドは公式ドキュメントが参考になります。

(2025/1追記):この記事を書いた2024/9頃はXSCTコマンドでXSDB(GDB的なやつ)を操作するようなドキュメント記述だったのですが、Vitis v2024.2現在では、Python XSDB APIへの移行が進められているようです。

Vitis Python API(UG1400, 2024.2)
Python XSDB コマンド(UG1400, 2024.2)

今回は以下の例をやってみます。

  • XSCTの起動とELFによるシンボル情報のロード
  • ブレークポイントの設定
  • ステップ実行
  • バックトレース
  • グローバル変数の表示
  • ローカル変数の表示
  • 複数コアデバッグ

XSCTの起動とELFによるシンボル情報のロード

既にOpenAMPなどでCR5用のELFのロード及び、CR5が実行中であるとします。その状態で母艦PCのShellで実行中のELFと同じファイルの存在するPathへ移動します。次にXSCTを起動します。

Bash
$ ls
app_echo_uart_r5_0.elf

#XSCT起動
$ xsct
#hwserverに接続
xsct% connect
#ターゲットCPUの選択
xsct% targets                                                                                                                                                                                              
  1  PS TAP
     2  PMU
     3  PL
        4  MicroBlaze Debug Module at USER2
           5  MicroBlaze #0 (Running)
  6  PSU
     7  RPU
        8  Cortex-R5 #0 (Running)
        9  Cortex-R5 #1 (Running)
    10  APU
       11  Cortex-A53 #0 (Running)
       12  Cortex-A53 #1 (Running)
       13  Cortex-A53 #2 (Running)
       14  Cortex-A53 #3 (Running)
#Cortex-R5 #0 (Running)を選択
xsct% targets 8 

#ELFシンボルをロード
xsct% memmap -file ./app_echo_uart_r5_0.elf 

#**********************************************
#切断、終了の仕方
xsct% disconnect
xsct% exit
$

ブレークポイントの設定

ブレークポイントの追加”bpadd”コマンドはいろいろ機能がありますが、とりあえず、以下の2つをやってみます。

  • 関数名で指定
  • ファイル名と行番号で指定

例として以下のLedOn関数にブレークポイントを貼ってみます。これはLED点滅処理をするものです。

関数名でブレークポイントを貼る

bpadd -addr &<関数名>

CR5が動作中であれば即ブレークします。

Bash
#ブレークポイントを貼る(関数名)
xsct% bpadd -addr &LedOn                                                                                                                                                                                   
0                                                                                                                                                                                                          
xsct% Info: Breakpoint 0 status:
   target 8: {Address: 0x3ed0081c Type: Hardware}
xsct% Info: Cortex-R5 #0 (target 8) Stopped at 0x3ed0081c (Breakpoint)                                                                                                                                     
LedOn() at led.c: 61
61: {

#ブレークしたか確認
xsct%  targets                                                                                                                                                                                              
  1  PS TAP
     2  PMU
     3  PL
        4  MicroBlaze Debug Module at USER2
           5  MicroBlaze #0 (Running)
  6  PSU
     7  RPU
        8* Cortex-R5 #0 (Breakpoint) <ー停止した
        9  Cortex-R5 #1 (Running)
    10  APU
       11  Cortex-A53 #0 (Running)
       12  Cortex-A53 #1 (Running)
       13  Cortex-A53 #2 (Running)
       14  Cortex-A53 #3 (Running)

##以下おまけ******************************************
#ブレークを外してCPUを再開する

#ブレークポイントを外すためにIDを調べる
xsct% bplist                                                                                                                                                                                               
ID        Enabled   Location            Status              
==        =======   ========            ======              
0         1         &LedOn              target 8: {Address 0x3ed0081c HitCoun
                                        t 1} 
#ブレークポイントID=0を外す
xsct% bpremove 0 

#コア動作を再開する
xsct% con                                                                                                                                                                                                  
Info: Cortex-R5 #0 (target 8) Running

ソースファイル名と行番号でブレークポイントを貼る

bpadd -file <ソースファイル名> -line <行番号>

CR5が動作中であれば即ブレークします。

Bash
#ブレークポイントを貼る(ファイル名と行番号)
xsct% bpadd -file main.c -line 167                                                                                                                                                                         
1
xsct% Info: Breakpoint 1 status:
   target 8: {Address: 0x3ed01050 Type: Hardware}
   target 8: {Address: 0x3ed01080 Type: Hardware}
xsct% Info: Cortex-R5 #0 (target 8) Stopped at 0x3ed01080 (Breakpoint)                                                                                                                                     
BlinkLedTask() at main.c: 167
167:         LedOn();

#CPU状態確認
xsct% targets                                                                                                                                                                                        
  1  PS TAP
     2  PMU
     3  PL
        4  MicroBlaze Debug Module at USER2
           5  MicroBlaze #0 (Running)
  6  PSU
     7  RPU
        8* Cortex-R5 #0 (Breakpoint) <ー停止した
        9  Cortex-R5 #1 (Running)
    10  APU
       11  Cortex-A53 #0 (Running)
       12  Cortex-A53 #1 (Running)
       13  Cortex-A53 #2 (Running)
       14  Cortex-A53 #3 (Running)
xsct%  

ステップ実行

ブレークした状態から、ステップ実行してみます。

ステップイン

stp <カウント:省略可>

LedOn関数で3回ステップインしています。”stp 3″で同じことが出来ます。

Bash
xsct% stp
Info: Cortex-R5 #0 (target 8) Stopped at 0x3ed01050 (Breakpoint)
167:         LedOn();

xsct% stp                                                                                           
Info: Cortex-R5 #0 (target 8) Stopped at 0x3ed00824 (Step)
LedOn() at led.c: 62
62:     if(initialize_done != TRUE) return eFAILURE;

xsct% stp                                                                                           
Info: Cortex-R5 #0 (target 8) Stopped at 0x3ed00840 (Step)
64:     XGpio_DiscreteWrite(&dev_led, 

xsct%  

ステップオーバー

nxt <カウント:省略可>

ステップオーバーします。

バックトレース

bt

Bash
xsct% bt                                                                                            
    0  0x3ed08488 XGpio_DiscreteWrite()
    1  0x3ed00854 LedOn()+56: led.c, line 64
    2  0x3ed01054 BlinkLedTask()+20: main.c, line 167
    3  0x0 _vector_table()
    4  unknown-pc
xsct% 

グローバル変数の表示

printコマンドで見れます。このコマンドは演算式が組めるようで高機能なのですが、そこには触れません。

表示:print <変数名>

値の書換:print -set <変数名> <設定する値>

構造体もメンバ展開してくれるようです。

Bash
#変数
xsct% print init_hardware_task                                                                      
init_hardware_task  : 1053988888

#構造体
xsct% print dev_led                                                                                 
dev_led              : XGpio
   BaseAddress       : 2147483648
   IsReady           : 286331153
   InterruptPresent  : 0
   IsDual            : 0

#値の書換
xsct% print initialize_done                                                                         
initialize_done  : 0
xsct% print -set initialize_done -1                                                                 
xsct% print initialize_done                                                                         
initialize_done  : -1

ローカル変数の表示

localsコマンドで現在のスタックフレーム内のローカル変数を表示できます。

ローカル変数の表示:locals

ローカル変数の書換:locals <変数名> <設定する値>

Bash
xsct% locals                                                                                  
pvParameters   : 0
get_bytes_cnt  : 0
buf_cnt        : 0

xsct% locals pvParameters 1                                                                         
xsct% locals                                                                                        
pvParameters   : 1
get_bytes_cnt  : 0
buf_cnt        : 0

複数コアデバッグ

複数コアデバッグする方法を説明します。

下準備

各コアごとにELFでシンボル情報を読み込むことが必要です。

  • targetsコマンドで操作対象CPUを指定
  • memmapコマンドでELFのシンボル情報をロード
  • これを各コアごとに行う

デバッグ

targetsコマンドで操作対象CPUを切り替えて、デバッグします。

4-2. Vitisから動作中のサブコアに接続

新Vitisからでも動作中のサブコアに接続してデバッグする機能はあるのですが、現状、ディスアセンブルコード単位でのデバッグとなります。公式ドキュメントにELFのシンボル情報をロードするための機能については特に言及していません。一応外部のスクリプトを実行できそうな設定は残っていますが、ドキュメントでは一切触れていないです。そのため、現状ではかなり使いにくそうと言った印象です。私が資料を読み飛ばしている可能性もあるので、また進展があれば、記事を更新したいと思います。

一応デバッグ設定のスクショを貼っておきます。

ABOUT ME
sh-goto
低レイヤで遊んでいます
関連記事