ベアメタルRaspberryPiでのOpenOCDとGDBによるJTAGデバッグ

この記事はkstmアドベントカレンダー2018 23日目の記事として書かれました.

qiita.com

はじめに

お久しぶりです.昨年のアドベントカレンダーの記事(URL)を見返したところ.

なにこれ...意味わからん...となってしまったので,今年は昨年の内容を補足・修正した上で, 今回は実際にOpenOCDによるJTAGデバッグを行っみたいと思います.

ベアメタルって何

この本を買って読みましょう.

tatsu-zine.com

要するにRaspberryPiなどのマイコンシングルボードコンピュータにて,あえてOSを用いずに直接CPUを動作させることです. そんなベアメタルのメリットですが,私は普段OSによって抽象化されているハードウェアがどのように制御されているのか, 実際に手を動かしながら学べるところにあると考えています. また,ベアメタルガチ勢の方々はCPUの性能を限界まで引き出せるような使い方をしているようです.かっこいい.

今回の記事は,すでにARMクロスコンパイル環境が完成している前提で進めていきます.クロスコンパイル環境の構築やベアメタルでのLチカ等は, 上記の本やインターネット上の記事を参考にしてください.

JTAGとは

https://ja.wikipedia.org/wiki/JTAG

マイコン等の検査やデバッグに用いられるアクセスポートの規格のようです. 今回はICE(In-Cercuit Emulator)の一種として,マイコンを動作させながら デバッグを行います.

OpenOCDとは

申し訳ないのですが,正直ここへの理解は曖昧です.FT232HL等のJTAGデバッガとGDBを接続しているモノと理解しています.

JTAG環境の構築

使用機材

akizukidenshi.com

環境

ハードウェアの接続

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ファイルは,下記ページからお借りしました.ありがとうございました.

github.com

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が好きではない人が多いと思いますが(勝手なイメージ),VScodeEclipseとの連携もしてみたいです.

参考文献

tatsu-zine.com