ベアメタルRaspberryPiでのOpenOCDとGDBによるJTAGデバッグ
この記事はkstmアドベントカレンダー2018 23日目の記事として書かれました.
はじめに
お久しぶりです.昨年のアドベントカレンダーの記事(URL)を見返したところ.
なにこれ...意味わからん...となってしまったので,今年は昨年の内容を補足・修正した上で, 今回は実際にOpenOCDによるJTAGデバッグを行っみたいと思います.
ベアメタルって何
この本を買って読みましょう.
要するにRaspberryPiなどのマイコン,シングルボードコンピュータにて,あえてOSを用いずに直接CPUを動作させることです. そんなベアメタルのメリットですが,私は普段OSによって抽象化されているハードウェアがどのように制御されているのか, 実際に手を動かしながら学べるところにあると考えています. また,ベアメタルガチ勢の方々はCPUの性能を限界まで引き出せるような使い方をしているようです.かっこいい.
今回の記事は,すでにARMクロスコンパイル環境が完成している前提で進めていきます.クロスコンパイル環境の構築やベアメタルでのLチカ等は, 上記の本やインターネット上の記事を参考にしてください.
JTAGとは
https://ja.wikipedia.org/wiki/JTAG
マイコン等の検査やデバッグに用いられるアクセスポートの規格のようです. 今回はICE(In-Cercuit Emulator)の一種として,マイコンを動作させながら デバッグを行います.
OpenOCDとは
申し訳ないのですが,正直ここへの理解は曖昧です.FT232HL等のJTAGデバッガとGDBを接続しているモノと理解しています.
JTAG環境の構築
使用機材
- Raspberry Pi A+
環境
- Ubuntu 18.10(仮想環境)
ハードウェアの接続
RPi | ft232HL |
---|---|
GPIO25 | AD0 |
GPIO4 | AD1 |
GPIO24 | AD2 |
GPIO27 | AD3 |
GPIO22 | AD4 |
GND | GND |
OpenOCDの導入
sudo apt install openocd
cfgファイルのダウンロード
OpenOCDを起動する際に,JTAGデバッガとターゲットマイコンの情報を記したcfgファイルが必要になります.
Raspberry Piのcfgファイルは,下記ページからお借りしました.ありがとうございました.
OpenOCDの起動
下記コマンドでOpenOCDを起動します.
sudo openocd -f /usr/share/openocd/scripts/interface/ftdi/um232h.cfg -f ~/work/rpi_bmetal/raspberrypi/armjtag/raspi.cfg
JTAGデバッガのcfgファイルとして,/usr/share/openocd/script/ftdi/にある um232h.cfgを使用しました.今回使用するFT232HLとは異なる気もするのですが ほぼ同じ仕様のICらしくとりあえず動いているので良しとします. Raspberry Pi用のものは先程ダウンロードしたものです. 成功時にはこのようなメッセージが表示されました.
Open On-Chip Debugger 0.10.0 Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html adapter speed: 1000 kHz none separate Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'. raspi.arm Info : clock speed 1000 kHz Info : JTAG tap: raspi.arm tap/device found: 0x07b7617f (mfg: 0x0bf (Broadcom), part: 0x7b76, ver: 0x0) Info : found ARM1176 Info : raspi.arm: hardware has 6 breakpoints, 2 watchpoints
GDBの起動
arm-unknown-eabi-gdb "デバッグ対象のelfファイル"
プログラムの実行や停止はホストとなるPCから行います. OpenOCDは起動したまま,別のタブかターミナルを開きGDBを起動します. OpenOCDはサーバとして待機しているのでそこへGDBを接続します. target remote 3333で 接続します.
(gdb) target remote localhost:3333
openOCD側ではこんなメッセージが
Info : accepting 'gdb' connection on tcp/3333
デバッグ環境が完成しました.
GDB便利!面白い!
GDBの勉強ついでにLチカ(LED点滅回路)で遊んでみました.
LEDの点滅部分だけ抜粋するとこんなコードです.
16 //RaspberryPi A+のactLEDに接続されているGPIO47を操作する 17 // GPIO47をOutputに 18 *(volatile unsigned int *)GPFSEL4 |= 0x01 << (3*7); 19 //変数観察用 20 volatile unsigned char LED_state = 'L'; 21 //mainループ 22 while(1){ 23 // GPIO47をLに 24 *(volatile unsigned int *)GPCLR1 |= 0x01 << 15; 25 LED_state = 'L'; 26 27 // GPIO47をHに 28 *(volatile unsigned int *)GPSET1 |= 0x01 << 15; 29 LED_state = 'H'; 30 }
JTAGデバッグを行うプログラムをコンパイルする際には,コンパイラの最適化をOFFにし デバッグオプションをつけるようにしてください.
(gdb) load
これで実行の準備ができました.
(gdb) continue
で普通にプログラムが動作します.(フリー実行)
接続して一回目はうまく動かないことがありますが,loadとcontinueをもう一度繰り返すと動きました.
step実行
(gdb) step
stepで1ステップずつプログラムを進められます. 繰り返しstepコマンドを入力するとwhileループ中の処理ごとに停止している様子が確認できます.
(gdb) step 25 LED_state = 'L'; (gdb) step 28 *(volatile unsigned int *)GPSET1 |= 0x01 << 15; (gdb) step 29 LED_state = 'H'; (gdb) step 30 } (gdb) step 24 *(volatile unsigned int *)GPCLR1 |= 0x01 << 15;
bleakポイント
(gdb)break main.c:"行番号"
指定した行でプログラムを停止できます.
(gdb) break main.c:24 Breakpoint 1 at 0x807c: file main.c, line 24.
(gdb) break main.c:28 Breakpoint 2 at 0x8098: file main.c, line 28.
watchポイント
(gdb) watch "変数名"
指定した変数に代入が行われたら停止します.
(gdb) watch LED_state
LED_stateに値が代入されるたびブレークします.
(gdb) continue Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. main () at main.c:30 30 } (gdb) continue Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. main () at main.c:28 28 *(volatile unsigned int *)GPSET1 |= 0x01 << 15;
変数の内容を見る
(gdb) print "変数名"
変数の中身を見ます.
(gdb) print LED_state $1 = 72 'H' (gdb) continue Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. main () at main.c:28 28 *(volatile unsigned int *)GPSET1 |= 0x01 << 15; (gdb) print LED_state $2 = 76 'L'
メモリの値を見る
(gdb) x "番地"
指定した番地のメモリの値を見ます. Raspberry Pi A+ではメモリの0x20200038番地にGPIO32番以降のHIGH/LOW状態が格納されているようです。
(gdb) x 0x20200038 0x20200038: 0x003f5eff (gdb) continue Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. main () at main.c:30 30 } (gdb) x 0x20200038 0x20200038: 0x003fdeff
上記の値を見ると、LED消灯時は
(gdb) x 0x20200038 0x20200038: 0x003f5eff
点灯時
(gdb) x 0x20200038 0x20200038: 0x003fdeff
0x003f5eff→0x003fdeffと15番目のビットの変化が確認できます。
JTAGデバッグは組み込み開発においてかなり強力なツールだと思うので今後も使っていきたいですね. kstmのメンバーはIDEが好きではない人が多いと思いますが(勝手なイメージ),VScodeやEclipseとの連携もしてみたいです.