OpenOCD がやってきた

# はじめに

この ["インテル® FPGA Advent Calendar 2021"](https://qiita.com/advent-calendar/2021/intelfpga) でもいくつか取り上げられていますが、RISC-V 版の Soft CPU コア その名も Nios® V processor (以下 Nios V) が Intel® Quartus® Prime Pro Edition 21.3 (以下QuartusPro v21.3) に入ってきました。それに伴い、Nios V のデバッガの仕組みとして、[OpenOCD](https://openocd.org/) が導入されています。もしや、この OpenOCD で、Nios V に限らず他の CPU でも USB Blaster を使った JTAG デバッグができるのでは？と思って試した内容になります。

試したのは Cyclone® V SoC FPGA[^4] の CPU (Arm Cortex-A9) です。 その際に使った Cyclone V SoC に接続するための OpenOCD 設定ファイルと、コードをダウンロード＆実行するための gdb debugger スクリプト、telnet 接続用の TCL script を掲載しています。
 [^4]: QuartusPro v21.3 ではサポートされていないデバイスとはなりますが、手ごろな FPGA で動いて欲しいと思ったのでチャレンジしてみた次第です。

# OpenOCD って

OpenOCD は、Open On-Chip Debugger の略で、"組み込みデバイスの debugging や in-system programming や boundary-scan を提供できることを目的とした"[^0]Open Source ソフトウェアです。
gdb server および telnet server の機能を持ち、多様なデバッグプロープをサポートすることで、多くの組み込み機器でのデバッグを可能にしています。基本的な使い方としては、Server としてバックグランドで起動しておき、クライアントソフトウェア (gdb や telnet クライアント) が、デバッグプロープと接続できるよう仲介役となります。

QuartusPro v21.3 に付属の OpenOCD では、USB Blaster をサポートしており、USB Blaster を使った gdb debugger による JTAG debug が、Nios V をターゲットとしてできるようになっています。ですが Nios V に限らず、 OpenOCD がサポートする CPU （Arm Cortex-A9 とか）に対しても可能になっているのでは、ということで調査&試してみました。
OpenOCD を使うためのセットアップですが、Windows版では、QuartusPro v21.3 のインストールが必要です。Linux版では、QuartusPro v21.3 の Programmer だけのインストールでも OpenOCD が使えるようになってます。OpenOCD を使うだけなので、Nios V 用ツールチェーンの追加インストールも必要ありません。もちろん、OpenOCD には有償のライセンスは必要なく、無償で使用可能です。

 [^0]: 公式サイトの[ What is OpenOCD ](https://openocd.org/doc/html/About.html#About) より。

# 全体の構成

試した内容について、その構成図を下に書きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175992/e896056b-89b7-3c41-7a12-9b58fcd5f716.png)



**Hardware:** デバッガを動作させる PC と FPGA ボードを JTAG (実際は OnBoard USB Blaster なので USB cable) で接続。PC は、Windows でも Linux でも OK。

**PC Software:** OpenOCD を起動させておき、USB Blaster をアクセスする gdb server が起動している状態にしておく。Arm CPU 用 gdb debugger を OpenOCD の gdb server に接続し、USB Blaster を使ったデバッグ(コードのダウンロード&実行) を実施。

# 動作確認に使ったボード
Cyclone V SoC が搭載された Terasic 社製 [ DE10-Nano kit ](https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&No=1046) を使用します。

# OpenOCD の起動

OpenOCD を起動しておくための shell として、Quartus v21.3 の Nios V Command Shell を起動します。

まずは、使用する PC で正常に DE10-Nano kit の USB Blaster が認識され、JTAG device として HPS と FPGA が存在するかを、確認します。SD card を挿していない状態のDE10-nano の J13 と PC を mini-USB ケーブルで接続し、DE10-nanoの電源を入れます。その後、```jtagconfig``` コマンドを実行します。

```:NiosV_Command_Shellにて
[niosv-shell] C:\intelFPGA_pro\21.3> jtagconfig
1) DE-SoC [USB-1]
  4BA00477   SOCVHPS
  02D020DD   5CSEBA6(.|ES)/5CSEMA6/..
```
この表示がされれば正常です。Arm CPU の入った HPS の JTAG ID が 4BA00477 であることが確認できます(Arria10 の HPS と同じ ID です)。

## OpenOCD 設定ファイルの作成
OpenOCD を使うには、使用するターゲットの環境を記述した設定ファイルが必要となります。書式としては、TCL に準拠しています。まずは、参考になるファイルを、QuartusPro v21.3 のインストールフォルダ内で探してみます。すると、
<install_folder>/quartus/bin64/oocd/openocd/scripts/board
という名前のフォルダがあり、そのフォルダの下に
 altera_arria10__aji_client.cfg
というファイルがありました。名前と中身のコメント等からすると Arria10 SoC の HPS に接続するための設定ファイルの様で、Arria10 SoC をデバッグするならこのファイルが使えるものと思われます。
で、これを参考に、今回のターゲット Cyclone V SoC への接続を試したところ、エラー無く、接続できるようになりました。設定ファイルは以下となります。ファイル名は、```cyclone5_client.cfg``` としています。

```tcl:cyclone5_client.cfg
###############################################################
# Point:  Driver として aji_client を指定
adapter driver aji_client

###############################################################
# Chip 名: 好きな名前に変更可(cvsoc としておきます)
set _CHIPNAME cvsoc

###############################################################
# HPS TAP(Test Access Port)/DAP(Debug Access Port) の JTAG ID
set _DAP_TAPID 0x4ba00477

###############################################################
# JTAG TAP(Test Access Port) として HPS DAP を宣言
jtag newtap $_CHIPNAME cpu -irlen 4 -expected-id $_DAP_TAPID

###############################################################
# HPS の TAP は ARM DAP(Debug Access Port) であることを宣言. 
# ARMv6-M, ARMv7, ARMv8 が target の時にやっておく
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu

###############################################################
# gdb debugger で操作する target は、 HPS DAP の core0 とする
target create $_CHIPNAME.cpu.0 cortex_a -dap $_CHIPNAME.dap -coreid 0 -dbgbase 0x80110000
```
何度か Try&Error を繰り返しての内容となりますが、結局、オリジナルの Arria10 用ファイルから、ほぼ、必要最小限の部分だけを抜き出した形にしています。一点、最後の target create... の行で ```-dbgbase 0x8011000```[^3]の記述を追加しています。
※ 場合によっては、日本語のコメントが問題を起こすかもしれません。その際はコメント行を削除してみてください。

 [^3]: この記述が無くても、FPGA がコンフィグされていない状態であれば、起動時にdbgbaseアドレスを自動検出して動作してくれます。デバッグオプション(-d)を付けると自動検出されたアドレスは Detected core 0 dbgbase: 80110000 と表示されました。一方、FPGA に JTAG を使う IP (SLD Node)がコンフィグされていると自動検出がうまくいかず、このオプション無しでは起動エラーとなってしまいます。

## OpenOCD の起動と確認
v21.3 の Nios V Command Shell を起動します。その Shell にて、先程作成した設定ファイル cyclone5_client.cfg を保存したフォルダに cd した後、 
```openocd -f cyclone5_client.cfg```を実行します。すると、以下の様に Log が表示された後、Log 表示は停止状態になります。

```:NiosV_Command_Shellにて
[niosv-shell] C:\de10nano\OpenOCD_download> openocd -f cyclone5_client.cfg
Open On-Chip Debugger 0.11.0-R21.3
Licensed under GNU GPL v2
...途中略...
Info : Listening on port 4444 for telnet connections
...途中略...
Info : At present, The first hardware cable will be used [1 cable(s) detected]
Info : Cable 1: device_name=(null), hw_name=DE-SoC, server=(null), port=USB-1, chain_id=0000019c3ca88620, persistent_id=1, chain_type=1, features=2048, server_version_info=Version 21.1.0 Build 842 10/21/2021 SJ Standard Edition
Info : TAP position 0 (4BA00477) has 0 SLD nodes
Info : TAP position 1 (2D020DD) has 0 SLD nodes
Info : Discovered 2 TAP devices
Info : Detected device (tap_position=0) device_id=4ba00477, instruction_length=4, features=0, device_name=SOCVHPS
Info : Found a ARM device at tap_position 0. Currently assume it is JTAG-DP capable
Info : Detected device (tap_position=1) device_id=02d020dd, instruction_length=10, features=4, device_name=5CSEBA6(.|ES)/5CSEMA6/..
Info : Found an Intel device at tap_position 1.Currently assuming it is SLD Hub
Info : This adapter doesn't support configurable speed
Info : JTAG tap: cvsoc.cpu tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd), part: 0xba00, ver: 0x4)
Info : JTAG tap: auto0.tap tap/device found: 0x02d020dd (mfg: 0x06e (Altera), part: 0x2d02, ver: 0x0)
Warn : AUTO auto0.tap - use "jtag newtap auto0 tap -irlen 10 -expected-id 0x02d020dd"
...途中略...
Info : starting gdb server for cvsoc.cpu.0 on 3333
Info : Listening on port 3333 for gdb connections
```
前半の Log から telnet server が起動され、4444 port で telnet 接続の待ち状態であること、後半の Info : JTAG tap: の行から、正しくデバイス ID が認識されていることが分かります。また最後の２行から、gdb server が起動され、3333 port で、gdb の接続待ちの状態になっていることも見て取れ、問題なく Cyclone V HPS と接続された gdb server が起動していることが確認できました。<br>
ちなみに、途中 Warn : AUTO auto0.tap ... と表示されているのは、cyclone5_client.cfg の記述において FPGA の JTAG TAP 定義を省略したため、ツールが自動で検出した旨を示しています。JTAG instruction length なども自動検出してくれてます。

# gdb debugger と接続
無事に OpenOCD が起動できたので、まずは、gdb debugger との接続を行ってみます。

## gdb の起動＆動作確認
gdb debugger を起動し、OpenOCD の gdb server と接続してみます。ここでは、SoCEDS v18.1 standard edition に付属の Arm 用 gdb を使います(基本的には、ターゲット cpu をサポートする gdb であれば使えるはずです、が、gdb で ELFファイルを読ませる場合は、ELF を作成した gcc ツールチェーンと同じバージョンの gdb を使わないと、シンボルテーブルが正しく読み込めないかもという懸念はあります)。ここで v18.1 を使っている理由はいくつかあるのですが、
 - DE10-nano のサンプルHWデザインが問題なくコンパイルできる(v18.1までなら、FrameReader Video IP が入っているサンプルがコンパイルできる）
 - Cygwin や、WSL を自分でダウンロード＆インストールしなくて良い
などがあります[^5]。
 [^5]: 最近の Windows version との組み合わせだと preloader の make がエラーになる場合がありますが、その場合の対策の一つとして、```export PATH=/usr/bin:$PATH``` として PATH 環境変数の先頭に /usr/bin を設定してやるというものがあります。

SocEDS command shell を起動します。そこから Arm 用のgdb である、arm-altera-eabi-gdb を起動します。このとき、OpenOCD の gdb server の port 3333 と接続するためのオプションをつけるのがポイントとなります。
```arm-altera-eabi-gdb -ex 'target extended-remote localhost:3333' ```

```:SOCEDS_Command_Shellにて
$ arm-altera-eabi-gdb -ex 'target extended-remote localhost:3333'
GNU gdb (Sourcery CodeBench Lite 2016.11-88) 7.11.1.20160608-git
Copyright (C) 2016 Free Software Foundation, Inc.
...途中略...
Remote debugging using localhost:3333
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x00002fa8 in ?? ()
(gdb)
```
無事に gdb server と localhost:3333 で接続し、cpu をアドレス 0x00002fa8 で停止させている状態になってます。
これに伴い、OpenOCD を起動中の Nios V command shell にも何行かメッセージが追加表示されます(ここでの例示は省略します）。


続けて、Arm CPU の状態が見えているか ```info reg``` コマンドで確認してみると、

```:続き_SOCEDS_Command_Shellにて
(gdb) info reg
r0             0x0      0
r1             0x0      0
r2             0xfffffee0       4294967008
r3             0xff704000       4285546496
r4             0x77857713       2005235475
r5             0xffd02000       4291829760
r6             0x76     118
r7             0xfffff014       4294963220
r8             0xfffff014       4294963220
r9             0x76     118
r10            0xffd02000       4291829760
r11            0xffd02000       4291829760
r12            0xa0002041       2684362817
sp             0xfffffed0       0xfffffed0
lr             0x3104   0x3104
pc             0x2fa8   0x2fa8
cpsr           0x600001d3       1610613203
(gdb)
```
一通りのレジスタ内容の確認ができます。

また print コマンドでシステムメモリへのアクセスも試してみます。ここでは、アドレス 0xffd080f0 にマップされた System Manager の crc レジスタを見ています。

```:続き_SOCEDS_Command_Shellにて
(gdb) print/x *0xffd080f0
$1 = 0xe763552a
(gdb)
```
[Cyclone V SoC のドキュメント](https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/hb/cyclone-v/cv_54001.pdf)の通り、Cold reset 後の初期値である 0xE763552A になっています。

この様に、一通りの gdb のコマンドが動作することが確認できました！

gdb は、q コマンドで終了させましょう。
OpenOCD も Ctrl-C をキー入力し、終了させます。


## gdb でのダウンロード＆実行スクリプト
では今回の最終目的である、コードのダウンロードと実行を gdb のスクリプトを使って行ってみます。以下、preloader のファイル形式やバージョンに応じたスクリプトを、いくつかご紹介します。
基本は、既に提供されている Arm DS(もしくは Arm DS-5) 用のスクリプト *.ds の内容を gdb 用にすれば良し、ということになります。

### ELF形式 preloader (v2013.01）
まずは、バージョン 2013.01 の preloader の ELF 形式ファイルをダウンロードし実行するスクリプトです。長らくの間(SoCEDS v18.1 まで)、SoCEDS に付属の preloader (u-boot)コードは、2013.01 のバージョンでした。preloader をビルドして出来上がった ELF 形式ファイル (spl/u-boot-spl) をターゲットデバイス(DE10-Nano の Cyclone V SoC) にダウンロードし、実行するスクリプトの例は以下となります（download_preloader_elf.gdb という名前で作成しています）。

```tcl:download_preloader_elf.gdb
set confirm off
set pagination off
restore <u-boot-spl の path> 
symbol-file -readnow <u-boot-spl の path> 

thbreak spl_boot_device
jump _start

#Stop watchdog timer
#permodrst Reg , reset watch dog timer
#set $permodrst = (int *)0xffd05014
#set *$permodrst = (*$permodrst) | (1<<6)
#set *$permodrst = (*$permodrst) & ~(1<<6)
```

<details><summary>このスクリプトの説明</summary>
ここでのポイントは、spl_boot_device という関数でブレークするように設定していることです。これは、preloader が次に起動する Application イメージをどこから読み込むのか(SD cord or QSPI or NAND)を調べる関数です。ここに到達した時点で、起動に必要な全ての初期化が終わっているので、デバッガでは、ここで停止させています。（これ以上実行を進めると、SD card が挿入されていないためエラーになります。）多くの場合、この後、Application をダウンロードしてデバッグする... といった流れになるかと思います。<br>
あと、コメントアウトしてある最終の３行(set ...)は、preloader で起動されている WatchDog タイマを停止させるための処置です。リセットマネージャーの WatchDogTimer0 のリセットビットをトグルし WatchDogTimer0 を停止させています。この後、Application を実行するなどの追加の作業を行う際、WatchDog タイマが起動したままだと WatchDog のタイムアウトにより Reset されてしまうので（原因不明のリセットが入力されてしまったように見えてしまいます）。それを防ぐためには、この最終３行のコメントを外してやってください。
</details>

このスクリプトを -x オプションで指定し、gdb を実行します。
```arm-altera-eabi-gdb -ex 'target extended-remote localhost:3333' -x download_preloader_elf.gdb```

**重要** この gdb コマンドの実行に先立って、DE10-Nano ボードの再起動[^1]、 OpenOCD の再起動を行って下さい。

 [^1]: CPU リセットコマンドを見つけられていないので、今のところ、ターゲットの手動リセットで代用しております。

```:SOCEDS_Command_Shellにて
$ arm-altera-eabi-gdb -ex 'target extended-remote localhost:3333' -x download_preloader.gdb
GNU gdb (Sourcery CodeBench Lite 2016.11-88) 7.11.1.20160608-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
...略...
Remote debugging using localhost:3333
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x00002fa4 in ?? ()
Restoring section .text (0xffff0000 to 0xffff6968)
Restoring section .rodata (0xffff6968 to 0xffff81f0)
Restoring section .data (0xffff81f0 to 0xffff90c4)
Hardware assisted breakpoint 1 at 0xffff1342
cvsoc.cpu.0 rev 0, partnum c09, arch f, variant 3, implementor 41

Temporary breakpoint 1, 0xffff1342 in spl_boot_device ()
(gdb)
```

このコマンドを実行する際、DE10-Nano のシリアルターミナルを起動しておくと、以下の表示が行われ、preloader が実行されたことが確認できます。

```:DE10-NanoのSerial_Console_terminalにて
U-Boot SPL 2013.01.01 (Dec 14 2021 - 13:59:40)
BOARD : Altera SOCFPGA Cyclone V Board
...略...
INFO : Watchdog enabled
SDRAM: Initializing MMR registers
SDRAM: Calibrating PHY
SEQ.C: Preparing to start memory calibration
SEQ.C: CALIBRATION PASSED
SDRAM: 1024 MiB
```

### ELF形式 preloader (v2021.04 以降）
preloader は、u-boot をベースとするコードとなっていますが、u-boot(および preloader) は、2021.04 以降のバージョンから デバイスツリーファイルを持つよう変更になっています。 これに伴いデバッガ用のダウンロードスクリプトにも変更が必要になっています。以下がそのスクリプトです。

```tcl:download_new_preloader.gdb
set confirm off
set pagination off
restore <u-boot-spl の path>
symbol-file -readnow <u-boot-spl の path>
restore u-boot-spl.dtb binary &__bss_end
thbreak spl_boot_device
jump _start

#Stop watchdog timer
#permodrst Reg , reset watch dog timer
#set $permodrst = (int *)0xffd05014
#set *$permodrst = (*$permodrst) | (1<<6)
#set *$permodrst = (*$permodrst) & ~(1<<6)
```
<details><summary>このスクリプトの説明</summary>
新しい preloader(u-boot)では、ELF ファイルとデバイスツリー(.dtb)をメモリにロードする必要があります。5行目の ```restore u-boot-spl.dtb binary &__bss_end``` デバイスツリーのロードを行っています。__bss_end は、preloader コードの終了アドレスで、その直後に .dtb を配置するというのが、このバージョンの preloader(u-boot)の仕様になっています。

</details>

### バイナリ形式 preloader-mkpimage.bin 
ブート用の SD card に書き込むための preloader のバイナリ形式ファイル preloader-mkpimage.bin を使って preloader の実行を行ってみるスクリプトです。
（Terasic が提供する DE10-Nano のサンプルデザイン DE10_NANO_SoC_GHRD フォルダに、このファイルが付属しているので、手っ取り早く、動作確認したい人のために）
最近のバージョン(2013.xx より新しいもの) で生成されるブートメディアに書き込むための preloader バイナリ形式ファイル u-boot-splx4.sfp もこのスクリプトで起動できます。

```tcl:download_preloader_mkpimage.gdb
set confirm off
set pagination off
restore <preloader-mkpimage.bin の path> binary 0xffff0000 0x0 0xefff

watch *0xff704004
jump *0xffff004c

#Stop watchdog timer
#permodrst Reg , reset watch dog timer
#set $permodrst = (int *)0xffd05014
#set *$permodrst = (*$permodrst) | (1<<6)
#set *$permodrst = (*$permodrst) & ~(1<<6)
```
<details><summary>このスクリプトの説明</summary>
ポイントとしては、まず、restore コマンドで読み込むファイルがバイナリのため、バイナリを書き込むメモリオフセットアドレス(0xffff0000)、バイナリファイルのスタート(0x0) 、バイナリファイルの終了位置(0xefff) を指定していることです。バイナリを書き込むメモリオフセットアドレス(0xffff0000) は、HPS の OnChipMemory の先頭アドレスです。バイナリファイルのスタートは先頭(0x0)から、終了は preloader コードの最大サイズ 0xefff です。preloader を停止させるには、watch point(指定したメモリアクセスが行われた時) を使っています。これは、コードがバイナリのため、シンボル情報がなく、spl_boot_device 関数のアドレスが分からないためです。代わりに SD card 読み書きのため、SD card controller にアクセスが行われた時に CPU を停止するようにしています。0xff704004 は、SD card controller のレジスタのアドレスです。実行の開始アドレスは、このバイナリフォーマットの仕様(bootrom で決められている仕様)により 0xffff004c となっています。

</details>


### preloader/Baremetal アプリの DL&実行
より実用的な例として、preloader をロード＆実行に続き、サンプルの Baremetal アプリケーションをロード＆実行するスクリプトになります。

```tcl:download_preloader_app.gdb
set confirm off
set pagination off
restore <u-boot-splのpath> 
symbol-file -readnow <u-boot-splのpath> 
thbreak spl_boot_device
jump _start

#Stop watchdog timer
#permodrst Reg , reset watch dog timer
set $permodrst = (int *)0xffd05014
set *$permodrst = (*$permodrst) | (1<<6)
set *$permodrst = (*$permodrst) & ~(1<<6)

# load and run Application
restore <アプリケーションELFファイルのpath>
symbol-file -readnow <アプリケーションELFファイルのpath>
jump _start
```
<details><summary>スクリプトの説明 </summary>
これまでに紹介した preloader をダウンロードするスクリプトに、アプリケーションの ELF ファイル(.axf)をダウウンロードし、実行するコマンドを追加したものです。 preloader 実行後に、WatchDog Timer を止める処理を有効にしています。本来、このWatchDog Timer 停止処理は、アプリケーションの冒頭で、行うべきものかと思います。SoCEDS 付属の Software Example ではこの処理が入っていないので、この処理を入れています。

</details>

<details><summary>SoCEDS 付属の Software Example をビルドするときの注意 </summary>
今回、v18.1 SoCEDS 付属の Software Example の Altera-SoCFPGA-HardwareLib-16550-CV-GNU (<SoCEDS_install_folder>/embedded/examples/software/Altera-SoCFPGA-HardwareLib-16550-CV-GNU)をコンパイルして動作確認してみました。 その際の注意点を。
オリジナルのサンプルコードは、Arm製デバッガを使って実行することを前提としており、printf 出力をデバッガのコンソールに表示するようになっています(semi-hosting と呼ばれる機能が使われている)。gdb debugger ではその機能を OFF にする必要があります。そのために Makefile の 46行目を ```LINKER_SCRIPT := cycloneV-dk-ram.ld``` に変更しました。

</details>

# telnet で接続
今度は、telnet で OpenOCDに接続してみます。telnet client からは、OpenOCD がサポートするコマンド[^9]が直接実行できます。また、Jim-Tcl という TCL interpreter が コマンドラインインターフェスとして走っていますので、TCL スクリプトの実行も可能です。
まず、OpenOCD を起動しておいて下さい。OpenOCD の telnet server はデフォルトで、port 4444 で接続を待機します。

 [^9]: コマンド詳細は [OpenOCD User's Guide](https://openocd.org/doc-release/html/index.html#Top)を参照. すべてのコマンドが実装されているわけではないです。

下の図は、Telnet client として Tera Term を使った場合の例で、起動の設定は図の様に、Host: localhost, Service Telnet, TCP port#: 4444を設定して "OK" をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175992/50e96f9a-9e84-e899-5b9a-51c9c03321f6.png)
数秒後に、下のように OpenOCD からの文字出力とコマンド入力のためのプロンプトが出てくれば接続成功です。

```:telnet_terminalにて
Open On-Chip Debugger
>
```
同時に、OpenOCD を起動している Shell ウインドウには、以下のログが一行追加表示されているはずです。

```:OpenOCD起動Shell_terminalにて
Info : accepting 'telnet' connection on tcp/4444
```
<br>
続いて CPU debug 関連の OpenOCD のコマンド3つ(halt, reg, mdw) を実行した例です。

```:telnet_terminalにて
> # Halt CPU
> halt
cvsoc.cpu.0 rev 0, partnum c09, arch f, variant 3, implementor 41
target halted in ARM state due to debug-request, current mode: Supervisor
cpsr: 0x600f0153 pc: 0x00101fac
MMU: disabled, D-Cache: disabled, I-Cache: enabled
>
>
> # print registers
> reg
===== ARM registers
(0) r0 (/32): 0x3fffdfa4 (dirty)
(1) r1 (/32): 0x3fffd300 (dirty)
(2) r2 (/32): 0x00000200
(3) r3 (/32): 0x00000000
(4) r4 (/32): 0x00000000
(5) r5 (/32): 0x00000000
(6) r6 (/32): 0x00000076
(7) r7 (/32): 0xfffff014
(8) r8 (/32): 0xffffffff
(9) r9 (/32): 0x00000005
(10) r10 (/32): 0xffd02000
(11) r11 (/32): 0x3fffd2ec
(12) r12 (/32): 0xffffffff
(13) sp_usr (/32)
(14) lr_usr (/32)
(15) pc (/32): 0x00101fac
(16) r8_fiq (/32)
(17) r9_fiq (/32)
...略...
(73) d29 (/64)
(74) d30 (/64
>
> # Read a memory mapped register
> mdw 0xffd05014
0xffd05014: 01b6c035
```

<br>
さらに、telnet 接続では、他の OpenOCD コマンドも使えるのでそれらを確認してみます(```scan_chain```, ```dap info```)。

```:telnet_terminalにて
> scan_chain
   TapName             Enabled  IdCode     Expected   IrLen IrCap IrMask
-- ------------------- -------- ---------- ---------- ----- ----- ------
 0 cvsoc.cpu              Y     0x4ba00477 0x4ba00477     4 0x01  0x03
 1 auto0.tap              Y     0x02d020dd 0x00000000    10 0x01  0x03

> dap info
DAP transaction stalled (WAIT) - slowing down
AP ID register 0x24770002
        Type is MEM-AP APB
MEM-AP BASE 0x80000000
        ROM table in legacy format
                Component base address 0x80000000
                Peripheral ID 0x00000ee001
                Designer is 0x0ee, Altera
                Part is 0x1, Unrecognized
                Component class is 0x1, ROM table
                MEMTYPE system memory not present: dedicated debug bus
        ROMTABLE[0x0] = 0x1003
                Component base address 0x80001000
                Peripheral ID 0x04001bb961
                Designer is 0x4bb, ARM Ltd
                Part is 0x961, CoreSight TMC (Trace Memory Controller)
                Component class is 0x9, CoreSight component
                Type is 0x32, Trace Link, FIFO, buffer
...略...
        [L01] ROMTABLE[0x20] = 0x0
        [L01]   End of ROM table
        ROMTABLE[0x24] = 0x0
                End of ROM table

> 
```
dap info では、Arm CoreSight DAP の ROM table の内容がずらりと出てきます。

## preloader/Baremetal アプリのの DL&実行 
preloader と baremetal アプリケーションのダウンロードと実行を行う tcl スクリプトで書いてみました。ファイル中のコメントに書いていますが、gdb ではツールがやってくれた シンボル名->アドレス の対応付けを自分で事前に調べておく必要があります[^7]。
download_preloader_app.tcl というファイル名でセーブしたら、telnet terminal の OpneOCD のプロンプトから ```source download_preloader_app.tcl``` で実行可能です。[...] で囲ったところは自分の環境に合わせて変更してください。ちなみに、ファイル path を相対 path で指定する場合には、OpenOCD を起動している Shell の current work directory からの相対 path を指定することになります。
gdb と同様、telnet 接続の実行に先立って、DE10-Nano ボードの再起動[^1]、 OpenOCD の再起動を行って下さい。

 [^7]: より良い方法をご存じの方いらっしゃいまいたら、コメント欄下さい m(--)m

```tcl:download_preloader_app.tcl
halt

###################################################################
# Preloader をダウンロード
# 相対pathで指定するときは、OpenOCD を起動した shell の pwd からの相対pathで.
load_image [preloaderのelfファイルpath]

###################################################################
# 実行開始アドレスのセット
reg pc 0xffff0000

###################################################################
# preloader 中の spl_boot_device関数のアドレスを breakpoint に設定
# このアドレスの調べ方の例:
#  $ arm-altera-eabi-readelf.exe -s [preloader elfファイルpath] | grep spl_boot_device
#
bp [spl_boot_device関数のアドレス] 2 hw 

###################################################################
# preloader 実行
resume
wait_halt

###################################################################
# WatchDog Timer のリセット
set permodrst 0xffd05014
set permodrst_val "0x"[lindex [mdw $permodrst] 1]
mww $permodrst [expr ($permodrst_val)|(1<<6)]
mww $permodrst $permodrst_val

###################################################################
# Baremetal application のロード
load_image [application elf の path]

###################################################################
# application の実行開始アドレスの探し方(SOCEDS 付属の Altera-SoCFPGA-HardwareLib-16550-CV-GNU の場合)
#    arm-altera-eabi-readelf.exe -s [application elfのpath] | grep _start
reg pc [application実行開始アドレス]

###################################################################
# Thumb2 モードのクリア 
# cpsr bit-5 : Thumb execution state bit - to be cleared 
set cpsr_val [lindex [reg cpsr] 2]
reg cpsr [expr ($cpsr_val)&(~(1<<5)&0xffffffff)]

###################################################################
# application 実行
resume
```


# まとめ

QuartusPro v21.3 に付属の OpenOCD で、Cyclone V SoC の Arm Cortex-A9 CPU へのコードのダウンロード&実行が gdb および telnet 接続で可能なことが確認できました。
**有償ライセンスが必要だった Intel SoC FPGA の JTAG Debug が 無償のツール でも可能となってきた**
ということになります。
（もちろん、コードのダウンロード速度や GUI 環境[^2]など使い勝手は有償のデバッガに軍配が上がりますけど。）

まだ 「公式ドキュメントでのサポートは今後に期待」 ですが、サポートドキュメントが出てくる時には、使い勝手の向上や機能強化がされているものと、勝手に期待してます。

 [^2]: VSCode, Eclipse と組み合わせれば自力で構築可能な気も。[VSCode+gdb+OpenOCDでARMの開発をやる](https://qiita.com/misodengaku/items/085692f1d2a2094d04f9) が参考になります。
 
# あとがき
OpenOCD という名前はチラホラ聞いてはいましたが、これまでは使ったことはなく、今回コンパイル済みのバイナリがツールに付いてきたのを機に、次期尚早ではありますが、試させていただきました。

まずの印象としては、
- JTAG debug が手軽に gdb でできて便利。
- telnet 接続、JTAG 周りでも色々できて面白そう. あと Tera Term の macro と組み合わせると便利に使えそう。
- Github でソースコードも公開([参考資料](#参考資料)）されていて、マニア？も楽しめそう。
といった感じです。

では、では。（ご指摘、コメントなど大歓迎です！）

# 参考資料
* [Open On-Chip Debugger 本家のサイト](https://openocd.org/)
* [intel/aji_openocd Github へのリンク](https://github.com/intel/aji_openocd)
* [intel/libaji_client Github へのリンク](https://github.com/intel/libaji_client)
* [Nios® V processor インストール、Hello World実行編](https://qiita.com/ys11qiitai/items/10a62072081341441e87)
* [Nios® V processor ブート、シミュレーション、デバッグ編](https://qiita.com/ys11qiitai/items/6cdd904f3c251de15877)
* [既存の NiosR II/e システムを NiosR V/m に移植してみた](https://qiita.com/oritin/items/af144f45b50679d2843c)

# **Notices & Disclaimers**
Intel technologies may require enabled hardware, software or service activation.
No product or component can be absolutely secure.
Your costs and results may vary.

© Intel Corporation. Intel, the Intel logo, and other Intel marks are trademarks of Intel Corporation or its subsidiaries. Other names and brands may be claimed as the property of others.

The products described may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request.​

Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade.