/home/tnishinaga/TechMEMO

日々行ったこと、面白かったことを書き留めます。

gnu-efiを使ってAARCH64/ARM64のUEFIサンプルアプリを動かしてみる

UEFIアプリを作るためのツールキットとしてはEDK2とgnu-efiの2つが有名ですが、後者のgnu-efiについて書いているところが少なかった気がしたので、サンプルビルドの方法とQEMU上で実行する手順について書いておくことにしました。

対象はx86_64ではなく、AARCH64(ARM64)です。クロスコンパイラを用意してから読んでください。

gnu-efiのビルド

ダウンロードする

以下からgnu-efiをもらってきます。

gnu-efi - Browse Files at SourceForge.net

バージョンは3.0.5が最新だったので、そちらをダウンロードしました。

解凍

tar xf gnu-efi-3.0.5.tar.bz2

ビルドする

cd gnu-efi-3.0.5

と移動して、CROSS_COMPILE変数を使ってクロスコンパイラの名前を教えてあげるとビルドができます。

make CROSS_COMPILE=aarch64-linux-gnu-

このとき、gccのバージョンが新しい(ver 7.x)の場合は、gnu-efiのコードのswitchにfallthrough(わざとbreakを挟まずに次の条件の処理を実行すること)しているところがあるので、次のようなエラーが出ます。

aarch64-linux-gnu-gcc -I/home/tnishinaga/gnu-efi-3.0.5/lib -I/home/tnishinaga/gnu-efi-3.0.5/lib/../inc -I/home/tnishinaga/gnu-efi-3.0.5/lib/../inc/aarch64 -I/home/tnishinaga/gnu-efi-3.0.5/lib/../inc/protocol -fpic  -g -O2 -Wall -Wextra -Werror -fshort-wchar -fno-strict-aliasing -fno-merge-constants -ffreestanding -fno-stack-protector -fno-stack-check -DCONFIG_aarch64 -c /home/tnishinaga/gnu-efi-3.0.5/lib/print.c -o print.o
/home/tnishinaga/gnu-efi-3.0.5/lib/print.c: In function '_Print':
/home/tnishinaga/gnu-efi-3.0.5/lib/print.c:1133:26: error: this statement may fall through [-Werror=implicit-fallthrough=]
                 Item.Pad = '0';
                 ~~~~~~~~~^~~~~
/home/tnishinaga/gnu-efi-3.0.5/lib/print.c:1134:13: note: here
             case 'x':
             ^~~~
cc1: all warnings being treated as errors

問題の詳細は以下を参照してください。

-Wimplicit-fallthrough in GCC 7 – RHD Blog

このエラーが起こった人は、makeの際に CFLAGS=-Werror=implicit-fallthrough=0を付けると、エラーを回避できます。

make CROSS_COMPILE=aarch64-linux-gnu- CFLAGS=-Werror=implicit-fallthrough=0

サンプルをビルドする

サンプルは以下のコマンドでビルドできます。

make CROSS_COMPILE=aarch64-linux-gnu- CFLAGS=-Werror=implicit-fallthrough=0 apps

ビルドさせたファイルは、gnu-efiディレクトリの下にある aarch64/apps に置かれています。

サンプルを動かしてみる

QEMUを使ってサンプルのUEFIアプリを起動してみましょう。

準備

OVMFの取得などの準備は、以下の記事を参照して行ってください。

tnishinaga.hatenablog.com

QEMUを起動する

以下のコマンドを実行すると、aarch64/apps以下が仮想マシンのドライブとしてマウントされた状態でUEFIシェルが起動します。

qemu-system-aarch64 -m 128 -cpu cortex-a57 -M virt -bios ../QEMU_EFI.fd -serial stdio -hda fat:rw:aarch64/apps

なお、ここで以下のエラーが出た人は、-hda fataarch64/appsの間に :rw: が正しく挟めているかを確認してください。

$ qemu-system-aarch64 -m 128 -cpu cortex-a57 -M virt -bios ../QEMU_EFI.fd -serial stdio -hda fat:aarch64/apps
vvfat aarch64/apps chs 1024,16,63
WARNING: Image format was not specified for 'json:{"fat-type": 0, "dir": "aarch64/apps", "driver": "vvfat", "floppy": false, "rw": false}' and probing guessed raw.
         Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
         Specify the 'raw' format explicitly to remove the restrictions.
qemu-system-aarch64: -hda fat:aarch64/apps: Block node is read-only

サンプルを実行する

UEFIシェル上で以下のコマンド(fs0:の部分)を実行すると、サンプルアプリのあるデバイスに移動できます。

Shell> fs0:

ここで、以下の用にするとHelloWorldサンプルが実行され、画面にHelloWorldが表示されます。

FS0:\> t.efi
HHello World!

f:id:tnishinaga:20170620191619p:plain

とりあえず以上です。

これからgnu-efiで自分のアプリを作る方法を調べようと思います。

先日U-Bootに投稿したARMv7MのLinux起動時にデータキャッシュを無効にするパッチについての備忘録

先日、以下のパッチをU-Bootに投稿し、マージされました。

[U-Boot] [PATCH] armv7m: Disable D-cache when booting nommu(ARMv7M) Linux kernel

忘れないように、なぜこのパッチを投稿したかを残しておこうと思います。

前回のあらすじ

ARM Cortex-M7マイコンの乗ったSTM32F746G-discovery board上でnommuなLinux kernelを動かすために色々頑張ってます。

解決したい課題

U-Bootからnommu Linux kernelのImageを起動すると、proc-v7m.Sの以下のsvc命令を実行するところで落ちる問題がありました。

 cpsie   i
    svc #0
1:  cpsid   i

linux/proc-v7m.S at master · torvalds/linux · GitHub

原因の心当たり

じつはこの問題のところ、以前私が触っていたときにも問題を起こした部分です。

当時はキャッシュを無効にした状態でcpsieとsvcの間にnopを10個ほど挟むと問題が解決するので、キャッシュかパイプラインが悪さをしているのではないかと考えていました。

その後、この問題は他の方が解決し、パッチを投稿し、マージされています。

github.com

今回はこのパッチがマージされた後の話ですが、前回の問題と似通っているので、今回も多分キャッシュとかパイプラインに問題があるのではとあたりをつけました。

問題の原因

nommu Linux起動の条件

MMUの無いARMv7MアーキテクチャのCPUで動作するnommu Linuxは、起動時にいくつか満たしていなければならない条件があり、その条件は linux/head-nommu.S at master · torvalds/linux · GitHub の頭にかかれています。その記述の一部を引用します。

The requirements are: MMU = off, D-cache = off, I-cache = dont care

ここで述べられているように、nommuなARMのLinux kernelはエントリポイントの時点ではMMUがoffかつデータ(D)キャッシュがoffである必要があります(命令(I)キャッシュは不問)。

なので、U-BootなどのブートローダーからLinux Kernelのエントリポイントに分岐する際は、データキャッシュをoffにしておく必要があります。

一方、U-Bootは……

U-Bootは2017年3月頃にARMv7MアーキテクチャのCPUでcacheを有効にするパッチが投稿・マージされたようで、現在は起動時に自動でデータ及び命令キャッシュの両方が有効になります。

[U-Boot] [PATCH 0/2] add armv7m cache support

しかし、Linux kernelのエントリポイントに飛ぶ処理を担当するboot_jump_linux関数を arch/arm/lib/bootm.c より読んでみると、ARMv7M上でLinuxを起動する場合にデータキャッシュを無効化を行っていませんでした。

解決方法

boot_jump_linux関数内にはARMv7Mアーキテクチャの場合はThumb命令を考慮してエントリポイントアドレスの下1桁を1にする処理が入っていました。なので、その部分にデータキャッシュを無効化するdcache_disable関数を追加しました。

これにより、ARMv7MアーキテクチャのCPU上でnommu Linux kernelが起動するようになりました。

投稿したパッチは既にU-Bootに取り込まれているため、Masterブランチのコードを使えば、キャッシュのあるARMv7Mマイコン上でもU-Bootを使ってLinuxを問題なく起動できるはずです。

以上です。

ARM命令はなぜ先頭に0xEが並ぶのかについて調べてみた

www.slideshare.net

こちらのスライドでご存知の方も多いと思いますが、ARMの実行バイナリをバイナリエディタのビットマップビューで見るとゴマ粒のようなものが縦に並んでいるのが見えます。

f:id:tnishinaga:20170519035405p:plain

これはほとんどの命令の先頭4bitが0xEになるというARM命令の特徴によるものであり、CTFなどでそのバイナリがどのアーキテクチャ向けのものかを特定する手がかりになります。

ここまでは私も知っていたのですが、ではなぜ命令の頭が0xEになるかまでは調べたことが無かったので、調べてみました。

条件実行

先頭が0xEとなる理由を探るため、ARM7の資料を参照してARM命令のフォーマットを確認してみます。

ARM Information Center

この資料より、ARM命令のフォーマットは大まかに以下のような構成になっていることがわかります。

| cond(4bit) | opcode | Rd | Rn | Operand2 |

多くが0xEとなっている先頭4bitの部分には、コンディション(Cond)フィールドがあります。 この部分は、ARM命令の特徴としてもよくあげられる、条件実行命令のために利用されます。

この条件実行命令がどういうものかというと、例えば「R0とR1の値が同じ値ならR0にR2を加算する」といったようなことをしようとしたとき、通常は以下のようなプログラムを書きます。

    cmp R0, R1
    beq addC
    b noaddC
addC:
    add R0, R0, R2
noaddC:
    nop

一方、ここで条件実行命令を使うと、以下のように書くことができます。

    cmp R0, R1
    addeq R0, R0, R2
    nop

このように、命令の後ろに条件を付けることで、特定の条件を満たす場合のみ、その命令を実行するといったことができます。 この条件実行命令の条件を指定する部分が、コンディションフィールドになります。

命令を常に実行したい場合は、条件によらず常に実行するというALコンディションを使いますが、このALコンディションは命令から省略することが可能です。 つまり、add命令と addal の命令は、コンパイルすると同じ命令が生成されることになります。

[tnishinaga@tx201 add]$ cat add.S
        add r0, r0, #0
        addal r0, r0, #0

[tnishinaga@tx201 add]$ arm-none-eabi-gcc -c -o add.o add.S 

[tnishinaga@tx201 add]$ arm-none-eabi-objdump -D add.o 

add.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <.text>:
   0:   e2800000        add     r0, r0, #0
   4:   e2800000        add     r0, r0, #0

このALを示すコードが0b1110であり、16進数では0xEとなります。 これが、ARM命令の先頭によく出てくる0xEの正体です。

ARMの命令は32bit固定長かつリトルエンディアンです。 なので、バイナリエディタで見ると0x*****Eのような値が規則正しく並び、ビットマップビューではこのEの部分が黒塗りとなるため、ゴマ粒が縦に綺麗に並んだバイナリを見ることができるのです。

まとめ

  • ARMバイナリの規則正しい黒ごまが見えるのは命令の最初が0xEから始まるため
  • 多くの命令の先頭が0xEなのは、コンディションフィールドの値がAL(0xE)だから

以上です。

間違い等あればコメントで指摘のほどよろしくお願いします。

参考

Arch LinuxのAndroid StudioでAndroidエミュレータが立ち上がらなかった件

数年ぶりに簡単なAndroidアプリを作ってみたくなったので、AndroidStudioをインストールしてとりあえずHelloWorldアプリを動かそうとしたのですが、AVDマネージャーからAndroidエミュレータの起動ボタンを押しても一向に端末の画面が出てきてくれる気配がありません。

今回はこの問題について、こうして原因特定して、調べて、動くようにしましたというメモです。

開発環境

OS Arch Linux(x86_64)
IDE AnrdoidStudio
AndroidSDKインストール ~/Android

問題

AVDマネージャーからAndroidエミュレータが起動しない。

原因特定

AndroidStudioのGUIから起動しようとすると何処にエラーログが出ているかわからなかったので、「たぶんCUIからもエミュレータの起動は可能で、CUIから起動すればエラーログも見えるでしょう」と考えてCUIで起動する方法を探しました。

CUIからのエミュレータ起動は、emulatorという実行バイナリを使って行えます。 これらはAndroidSDKのtoolsディレクトリ以下(こちらの環境では~/Android/Sdk/tools)にありました。

emulatorのhelpを見ると、AVDマネージャで作成したデバイス名を引数に渡すと良いようです。

$ ./emulator --help
emulator: ERROR: No AVD specified. Use '@foo' or '-avd foo' to launch a virtual device named 'foo'

AVDマネージャで作成したデバイス一覧は、tools/bin以下のavdmanagerにlist avdと指定すると教えてくれます。 私の環境で実行した結果は以下のような感じ。

 ./bin/avdmanager list avd
Available Android Virtual Devices:
Name: Nexus_S_API_25
Device: Nexus S (Google)
Path: /home/tnishinaga/.android/avd/Nexus_5X_API_25.avd
Target: Google APIs (Google Inc.)
Based on: Android 7.1.1 (Nougat) Tag/ABI: google_apis/x86
Skin: nexus_s
Sdcard: /home/tnishinaga/.android/avd/Nexus_5X_API_25.avd/sdcard.img

後はエミュレータを起動するだけです。

$ ./emulator -avd Nexus_S_API_25
sh: glxinfo: command not found
sh: glxinfo: command not found
emulator: ERROR: Failed to retrieve DNS servers list from the system
emulator: WARNING: Cannot find system DNS servers! Name resolution will be disabled.
emulator: WARNING: encryption is off
emulator: WARNING: UpdateCheck: Failure: Error
libpng warning: iCCP: known incorrect sRGB profile
libpng warning: iCCP: known incorrect sRGB profile
libpng warning: iCCP: known incorrect sRGB profile
libpng warning: iCCP: known incorrect sRGB profile
libpng warning: iCCP: known incorrect sRGB profile
libpng warning: iCCP: known incorrect sRGB profile
libpng warning: iCCP: known incorrect sRGB profile
libGL error: unable to load driver: i965_dri.so
libGL error: driver pointer missing
libGL error: failed to load driver: i965
libGL error: unable to load driver: i965_dri.so
libGL error: driver pointer missing
libGL error: failed to load driver: i965
libGL error: unable to load driver: swrast_dri.so
libGL error: failed to load driver: swrast
X Error of failed request:  BadValue (integer parameter out of range for operation)
Major opcode of failed request:  156 (GLX)
Minor opcode of failed request:  24 (X_GLXCreateNewContext)
Value in failed request:  0x0
Serial number of failed request:  42
Current serial number in output stream:  43
QObject::~QObject: Timers cannot be stopped from another thread
Segmentation fault (core dumped)

エラーのようなのは以下の3つ:

  • sh: glxinfo: command not found
  • emulator: ERROR: Failed to retrieve DNS servers list from the system
  • X Error of failed request: BadValue (integer parameter out of range for operation)

glxinfoが無いと言われる件は、yaourtでAURからインストールすれば解決しますが、それでもエミュレータは起動しません。

DNSもどうのう言ってますが、それだけでエミュレータがセグフォするとは考えられないので無視します。

最後のX周りのエラーが怪しいです。

調査と解決

Xのところをエラー文でググると、それっぽいものがヒット。

stackoverflow.com

libstdc++のことが取り上げられてますが、X周りならGraphics系じゃないかなーと描画方法をAutomaticからSoftwareに変更したところ、動くようになりました。

解決方法

f:id:tnishinaga:20170504025017p:plain

Emulated PerformanceのGraphicsのところをSoftwareにする。

ベストな解決方法じゃないかもしれませんが、とりあえず動けば良いのでこれで開発を続けようと思います。

ベストな方法をご存じの方はコメントで教えていただけると嬉しいです。

以上。

QEMUでARM64(AARCH644)のUEFIコンソールを実行する

UEFIといえばパソコンのBIOSに変わるファームウェアが真っ先に思い浮かびますが、最近ではHiKey boardなどARM64(AARCH64)の乗ったボードのファームウェアにもUEFIが使われ始めています。

UEFIを採用したボード上で動作するUEFIアプリケーションや、UEFIから起動するOSなどを作るために、開発中は何度も再起動やプログラムの転送等を行う必要があるでしょう。 しかし、この作業を実機の上で行うと手間がかかりますし、接続できるデバッガを持っていない場合はデバッグも不便です。

そこで今回は、QEMUを用いてARM64のUEFIアプリケーション開発等をエミュレータ上で行えるようにする方法について調べ、ここに記します。

実行環境

マシン ThinkPad x201
CPU Intel® Core™ i5 CPU M560 @ 2.67GHz
RAM 8GB
OS Arch Linux x86_64

手順

ARM64向けUEFI(以降、ARM64 UEFI)をQEMUで動かす方法は、ビルド済みイメージを用いる方法と、ソースからビルドを行う方法の2種類があります。

ビルド済みイメージを用いる方法は、以下のようなことを望む方におすすめです。

  • 手軽にARM64 UEFIを試したい方
  • UEFI上で動作するアプリケーションの開発・デバッグを行いたい方

ソースからビルドを行う方法は、以下のようなことを望む方におすすめです。

  • ソースからビルドする方法を知りたい方

ソースからビルドすればUEFIファームウェア自体をデバッグすることも可能かもしれませんが、やったことがないのでわかりません。

手順(共通ルート)

ARM64向けのQEMUをインストールする

Arch LinuxではPacmanで簡単に入ります

sudo pacman -S qemu-arch-extra

手順(ビルド済みイメージを使う方法)

UEFI(ARM64)のQEMU向け仮想マシンイメージを持ってくる

通常、QEMUで動作するUEFIイメージは、EDK2などを用いて自分でビルドを行う必要がありますが、ビルド済みイメージも配布されています。

x86用のビルド済みイメージであればPacmanからすぐにインストールできますが、ARM64向けのイメージはパッケージが用意されていません。 なので、ビルド済みイメージを以下のようにしてダウンロードします。

wget http://snapshots.linaro.org/components/kernel/leg-virt-tianocore-edk2-upstream/latest/QEMU-AARCH64/RELEASE_GCC5/QEMU_EFI.fd

手順(ソースからビルドを行う方法)

ビルドに必要な環境をインストールします。

sudo pacman -S aarch64-linux-gnu-binutils \
    aarch64-linux-gnu-gcc \
    aarch64-linux-gnu-gdb \
    aarch64-linux-gnu-glibc \
    aarch64-linux-gnu-linux-api-headers \
    iasl

edk2をクローンします。

git clone https://github.com/tianocore/edk2

BaseToolsをビルドします。

cd edk2
make -C BaseTools

環境設定をします。

source edksetup.sh
export GCC49_AARCH64_PREFIX=aarch64-linux-gnu-

最後にQEMU用のUEFIイメージをビルドします。

build -a AARCH64 -t GCC49 -p ArmVirtPkg/ArmVirtQemu.dsc

ビルドが完了するとDoneといわれます。 ビルドしたQEMU用のイメージは、Build/ArmVirtQemu-AARCH64/DEBUG_GCC49/FV/QEMU_EFI.fdにあります。

手順(再び共通ルート)

QEMUでARM64 UEFIを起動する

ダウンロード、またはビルドした QEMU_EFI.fd のあるディレクトリで以下のコマンドを実行することで、QEMU上でARM64 UEFIを起動することができます。

終了する際は、別窓で開くQEMUコンソールにqと打ち込んでエンターを押すと終了できます。

qemu-system-aarch64 -m 512 -cpu cortex-a57 -M virt -bios QEMU_EFI.fd -serial stdio

グラフィックが必要ない方は以下のコマンドを実行します。

終了する際は、起動したシェルの上で Ctrl + a, x の順に入力すると終了できます。

qemu-system-aarch64 -m 512 -cpu cortex-a57 -M virt -bios QEMU_EFI.fd -nographic

f:id:tnishinaga:20170317035453p:plain

起動時間は、私のマシン(初代Core i5@M540 2.67GHz)ではUEFIシェルが出てくるまでに1~2分ほどかかりました。

最近の性能の良いマシンだと、もう少し起動が速いかもしれません(性能の良いマシンがほしい……)

以上です。

参考情報

EDK2を用いてARM64向けのUEFIファームウェアをビルドする方法やQEMUで起動する方法などについて書かれています。

UEFIアプリケーションをGDBデバッグする方法が書かれています。

ここで紹介されている方法は以下の手順でデバッグを行います。

  1. UEFIアプリにアプリケーションのロードされた場所(ImageBase)を表示させる
  2. 停止させる
  3. デバッガを仮想マシンに接続する
  4. デバッガへImageBaseアドレスを用いてシンボル情報を適切な位置に読み込ませる
  5. 手順2.で停止させたところからデバッグを開始する

UEFIアプリが読み込まれるアドレスが不明のため、このようなことを行う必要があります。

すごく面倒なので何か自動化したいですが……何か良い方法はないものか……

2017年版おすすめ本のご紹介

蔵書の写真をTwitterにあげたところ思いの他反響があったので,おすすめの書籍を紹介したいと思います.

これから紹介する本は,私が今まで一部でも良いので読んだ本の内,所有しているもの,または記憶に残っているものになります.

UNIX, Linux系学習用

はじめてUNIXで仕事する人が読む本

ログイン方法からsed, grepなどの使い方,ネットワークやセキュリティの説明など,UNIXLinux環境での基本的な作業のやり方や,そのために必要な基本的な知識が網羅された本です.

この本に書かれている内容を覚えれば,基本的なLinuxのサーバー管理等で困ることは無いと思います.

コンピュータ・アーキテクチャ(低レイヤー)

パターソン&ヘネシー コンピュータの構成と設計(上・下)

コンピュータを理解する上で必要な2進数の話から,コンピュータを構成する要素や高速化の手法など,コンピュータ・アーキテクチャに関する知識が網羅された本です.

ただし,この本は全てが少し深めに書かれている上に1ページの文字数も多いので,読んでいて結構疲れます.

読んでいて難しいな,読むのが辛いなと思ったらもっと簡単なコンピュータアーキテクチャについて書かれた本を読んだり,マイコン開発をするなどして知識をつけてから読んだほうが良いかもしれません.

リンカ・ローダ実践開発テクニック

オブジェクト同士を結合し,実行可能なプログラムを作るリンカについて書かれた本です.

OS上の実行バイナリ(ELF)の構造やリンカスクリプトについて日本語で知りたい場合はまずこの本を読むと良いと思います.

著者の坂井さんの書かれた本では,対象の仕組みを説明するだけでなく,実際にそれを作ってみて理解を深めていくというということをよくされており,この本も途中から実際にリンカやローダーを自作することを行っていきます.

なので,仕組みを知りたいだけの方は前半のみ,手を動かして深く知りたい方は後半も含め読むと良い本だと思います.

ARMで学ぶアセンブリ言語入門

ARM(AARCH32)アセンブリコードの書き方,QEMU上での実行方法,基本的なARMアセンブリ命令の解説がわかりやすく書かれている本です.

ARMアセンブリコードを書く際の辞書として手元に1冊用意しておきたいです.

Raspberry Piで学ぶ ARM デバイスドライバープログラミング

Raspberry Pi上でLinuxデバイスドライバを書き,自作ハードウエアを制御する本です.

実はほとんど読めていないので詳細は書けませんが,Linuxデバイスドライバ作成の参考になる本だと思います.

Hacker’s Delight

ビットカウント演算やa<x<bの比較を1つの条件式で行う方法など,様々な黒魔術の書かれた本です.

黒魔術を使ってでもコードを高速化したい場合,例えばマイコン向けの高速なアセンブリコードを書く場合などを書く際に,辞書として利用するのが良いと思います.

目次は以下で公開されています.

http://www.hackersdelight.org/hacker2TOC.pdf

30日でできる!OS自作入門

x86(BIOS)環境上で動作するOSを作っていく本です.

比較的早いうちから画面描画を行うので作っていて楽しいですし,C言語のプログラムの書き方など必要な知識も一緒に教えてくれるので大変親切で読みやすいです.

スーパコンピュータ関係

絵でわかるスーパーコンピュータ

そもそもスパコンってなに? パソコンと何が違うの? どういうことができる? あると私たちにとってどのような良いことがあるの? といったことが絵を交えてわかりやすく書かれた本です.

大まかにスパコンについて知りたい方におすすめです.

スパコンプログラミング入門

スパコンプログラミングとありますが,正確には大規模並列計算を行うプログラムの書き方やアルゴリズムなどが書かれた本です.

MPIを使って通信を行う並列計算プログラミング方法も学ぶことができます.

ネットワーク関連

ルーター自作で学ぶ パケットの仕組み

その名の通り,Linux上でRawソケットを用いてイーサネットのパケットを受け取り,ルーティング等を行うルーターC言語で自作していく本です.

ルーターがどのようなことを行っているか,パケットの構造はどのようになっているのかなどを手を動かしながら学ぶことができます.

マスタリングTCP/IPシリーズ

ネットワークに関係する規格などが網羅的に書かれた本です.

よく注目を浴びる部分だけでなく関係する多くのことが細かく解説されて居る上,対応するRFCの番号なども示されているため深く知りたい場合はこの番号を頼りに調べられるようになっています.

一方,パターソン&ヘネシーと同様に内容が濃いので読んでいて疲れます.別の書籍を読んで全体を俯瞰して見た後,詳しく知りたい部分についてこの本を使って調べるのも良いかもしれません.

プログラミング関係

Beautiful code

プログラムをきれいに書いていくためのテクニックが書かれた本です.

自他共に読みやすいコードを書くため,一度は読むべき本と思います.

作文技術関係

数学文章作法(基本編・推敲編)

どのようにすれば自分の書きたいことを理論的に筋道立てて書くことができるかについて書かれた本です.

人に伝える文章を書く必要のある方におすすめの本です.

おわり

以上です. 学習の手助けになれば幸いです.

QEMUでCortex-M3/M4マイコンボードをエミュレーションしてLチカする話

Cortex-Mマイコン上で計算を行うアセンブリコードのデバッグを、エミュレータを使ってパソコンの上で行いたいです。 大学までのバスの中でパソコンからマイコン垂らしながらデバッグするのは不便で少し恥ずかしいので。

Cortex-M3プロセッサに対応しているエミュレータとしてQEMUがありますが、用意されているマイコンボードが LM3S811EVB / LM3S6965EVB の2つしかありません。

wiki.qemu.org

私は普段シリアルの設定などちょっと大変なところはすべてmbedライブラリにおまかせしているので、できればmbedの対応しているボードを使いたいです。 mbedに対応したマイコンボードの使えるエミュレータはないのでしょうか? ということで調べてみたのが今回の内容となります。

まとめ

  • GNU ARM Eclipse QEMUを使えばmbed対応ボードのNUCLEO-F103RBのエミュレーションができる
  • mbedライブラリに一部修正を加えることでLチカは動作した
  • TimerやUARTは今の所利用方法不明。要調査。

GNU ARM Eclipse

mbed対応ボードのエミュレーションができるツールを探していたところ、GNU ARM Eclipseというプロジェクトを見つけました。 こちらはARM開発のためのツールやEclipseプラグインなどを開発するプロジェクトのようです。

この中の一つにCortex-Mマイコン対応を増やしたQEMU forkであるGNU ARM Eclipse QEMUがあり、mbed対応ボードも2つほど サポートボードリストに含まれています。 これを使えばmbedライブラリを使いつつ、Cortex-Mマイコンのプログラムをエミュレータで実行できるかもしれません。 早速試してみましょう。

実行環境

以降の作業はThinkPad x201(x86_64、RAM 8GB)のArch Linuxで行いました。

対象ボードはNUCLEO-F103RBを選択します。

GNU ARM Eclipse のインストール

基本は以下を参照です。

gnuarmeclipse.github.io

Windowsの方はEXEファイルをダウンロードしてインストールするだけ。 Macの方はpkgファイルをダウンロードしてインストールするだけ。 Linuxの方はtgzファイルをダウンロードして適当なディレクトリで解凍するだけです。

私の使っているArch LinuxではAURにパッケージがあったので、yaourt を使ってgnuarmeclipse-qemu-binをインストールするだけでインストール完了しました。

$ yaourt -S gnuarmeclipse-qemu-bin

インストールしたファイルは /opt/gnuarmeclipse/qemu/ 以下に置かれます。

Lチカプログラムの作成

Lチカのプログラムはmbedのサンプルを作成してGCC用にエクスポートしてビルドするだけ……だと良かったのですが、 うまくいかないのでビルドを通すための修正を加えた後、QEMUで動くように修正を加える必要がありました。

※このときのmbed-srcのリビジョンはこちらの表記で635:a11c0372f0baでした

mbed-srcライブラリのインポート

まずはじめにmbedコンパイラでmbed_blinkyサンプルを作り、mbedライブラリをmbed-srcライブラリに置き換え、GCC用にエクスポートを行ってください。 mbed-srcライブラリのインポート方法については、以下のページにわかりやすく書かれています。

nora66.com

Makefileの修正

エクスポートしたソースを解凍した後、ビルドが通るようにするためMakefileに以下の修正を加えます。

  1. CC_FLAGSとCPPC_FLAGSとCC_SYMBOLSに書かれた -DDEVICE_SERIAL_ASYNCH=1 を -DDEVICE_SERIAL_ASYNCH=0 に変更する
  2. CC_FLAGSとCPPC_FLAGSとCC_SYMBOLSに書かれた -DDEVICE_CAN=1 を -DDEVICE_CAN=0 に変更する
  3. LD_FLAGSから-Wl,--wrap,main -Wl,--wrap,malloc_r -Wl,--wrap,free_r -Wl,--wrap,realloc_r -Wl,--wrap,calloc_rを削除する

1.はビルド時に出る以下のエラーを回避します。

../mbed-src/targets/hal/TARGET_STM/TARGET_STM32F1/serial_api.c: In function 'serial_break_set':
../mbed-src/targets/hal/TARGET_STM/TARGET_STM32F1/serial_api.c:337:48: error: 'serial_t {aka struct <anonymous>}' has no member named 'uart'
     UartHandle.Instance = (USART_TypeDef *)(obj->uart);

DEVICE_SERIAL_ASYNCHが有効になっている場合、serial_api.hで定義されているserial_t型の中身がuartメンバを持つserial_s型でなくなるために起こるエラーです。 根本的な解決は大変そうなので、今回はDEVICE_SERIAL_ASYNCHを0にして対応します。

2.は以下のエラーを回避します。

In file included from ../mbed-src/api/mbed.h:50:0,
                 from ../main.cpp:1:
../mbed-src/api/CAN.h:235:21: error: field '_can' has incomplete type 'can_t {aka can_s}'
     can_t           _can;
                     ^~~~
In file included from ../mbed-src/api/CAN.h:23:0,
                 from ../mbed-src/api/mbed.h:50,
                 from ../main.cpp:1:
../mbed-src/hal/can_api.h:55:16: note: forward declaration of 'can_t {aka struct can_s}'
 typedef struct can_s can_t;

C++詳しくなくてすぐに理由がわからないので、DEVICE_CANを0にして対応します。

3.を行わない場合、main等のラッパ関数を用意してないので以下のエラーが出ます。 ラッパ関数は特に必要ない(と思う)ので、オプションを削除して対応します。

/usr/lib/gcc/arm-none-eabi/6.3.0/../../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-fclose.o): In function `_fclose_r':
fclose.c:(.text._fclose_r+0x4a): undefined reference to `__wrap__free_r'
fclose.c:(.text._fclose_r+0x58): undefined reference to `__wrap__free_r'
fclose.c:(.text._fclose_r+0x7a): undefined reference to `__wrap__free_r'
/usr/lib/gcc/arm-none-eabi/6.3.0/../../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-fflush.o): In function `__sflush_r':
fflush.c:(.text.__sflush_r+0x7c): undefined reference to `__wrap__free_r'
/usr/lib/gcc/arm-none-eabi/6.3.0/../../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-fvwrite.o): In function `__sfvwrite_r':
fvwrite.c:(.text.__sfvwrite_r+0x22c): undefined reference to `__wrap__malloc_r'
fvwrite.c:(.text.__sfvwrite_r+0x2b4): undefined reference to `__wrap__realloc_r'
fvwrite.c:(.text.__sfvwrite_r+0x2c4): undefined reference to `__wrap__free_r'
/usr/lib/gcc/arm-none-eabi/6.3.0/../../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-makebuf.o): In function `__smakebuf_r':
makebuf.c:(.text.__smakebuf_r+0x2c): undefined reference to `__wrap__malloc_r'
/usr/lib/gcc/arm-none-eabi/6.3.0/../../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-malloc.o): In function `malloc':
malloc.c:(.text.malloc+0x6): undefined reference to `__wrap__malloc_r'
/usr/lib/gcc/arm-none-eabi/6.3.0/../../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-malloc.o): In function `free':
malloc.c:(.text.free+0x6): undefined reference to `__wrap__free_r'
/usr/lib/gcc/arm-none-eabi/6.3.0/../../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-wsetup.o): In function `__swsetup_r':
wsetup.c:(.text.__swsetup_r+0x98): undefined reference to `__wrap__free_r'
/usr/lib/gcc/arm-none-eabi/6.3.0/../../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-mprec.o): In function `_Balloc':
mprec.c:(.text._Balloc+0x22): undefined reference to `__wrap__calloc_r'
mprec.c:(.text._Balloc+0x3e): undefined reference to `__wrap__calloc_r'
collect2: error: ld returned 1 exit status

クロックソースの変更

クロックソースは内蔵RCオシレータ(HSI)にしないと動かなかったので、system_stm32f1xx.cのUSE_PLL_HSE_EXTCとUSE_PLL_HSE_XTALを0にセットします。

#define USE_PLL_HSE_EXTC (0) /* Use external clock */
#define USE_PLL_HSE_XTAL (0) /* Use external xtal */

C言語ソースの修正

wait関数(というよりTimer関係)がうまく動いてくれないので、Lチカプログラムのwait関数をfor文の空回しに変更します。

#include "mbed.h"

DigitalOut myled(LED1);

int main() {
    
    while(1) {
        myled = 1;
        for (int i = 0; i < 100000000; i++);
        myled = 0;
        for (int i = 0; i < 100000000; i++);
    }
}

修正に関するdiff

上記修正前と修正後のdiffを以下に示します。

diff --git a/Makefile b/Makefile
index d4de525..567fecf 100644
--- a/Makefile
+++ b/Makefile
@@ -50,19 +50,20 @@ SIZE    = $(GCC_BIN)arm-none-eabi-size
 
 
 CPU = -mcpu=cortex-m3 -mthumb 
-CC_FLAGS = -c -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fmessage-length=0 -fno-exceptions -fno-builtin -ffunction-sections -fdata-sections -funsigned-char -MMD -fno-delete-null-pointer-checks -fomit-frame-pointer -mcpu=cortex-m3 -mthumb -Os -std=gnu99 -D__MBED__=1 -DDEVICE_I2CSLAVE=1 -DTARGET_LIKE_MBED -DDEVICE_PORTINOUT=1 -D__MBED_CMSIS_RTOS_CM -DTARGET_STM32F1 -DDEVICE_RTC=1 -DTOOLCHAIN_object -DDEVICE_SERIAL_ASYNCH=1 -D__CMSIS_RTOS -DMBED_BUILD_TIMESTAMP=1483112652.49 -DTOOLCHAIN_GCC -DDEVICE_CAN=1 -DTARGET_LIKE_CORTEX_M3 -DTARGET_CORTEX_M -DARM_MATH_CM3 -DTARGET_UVISOR_UNSUPPORTED -DTARGET_M3 -DDEVICE_SERIAL=1 -DDEVICE_INTERRUPTIN=1 -DDEVICE_I2C=1 -DDEVICE_PORTOUT=1 -D__CORTEX_M3 -DDEVICE_STDIO_MESSAGES=1 -DTARGET_FF_MORPHO -DTARGET_FF_ARDUINO -DTARGET_RELEASE -DTARGET_STM -DTOOLCHAIN_GCC_ARM -DDEVICE_PORTIN=1 -DDEVICE_SLEEP=1 -DTARGET_NUCLEO_F103RB -DDEVICE_SPI=1 -DDEVICE_SPISLAVE=1 -DDEVICE_ANALOGIN=1 -DDEVICE_PWMOUT=1 -DTARGET_STM32F103RB -include mbed_config.h -MMD -MP
-CPPC_FLAGS = -c -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fmessage-length=0 -fno-exceptions -fno-builtin -ffunction-sections -fdata-sections -funsigned-char -MMD -fno-delete-null-pointer-checks -fomit-frame-pointer -mcpu=cortex-m3 -mthumb -Os -std=gnu++98 -fno-rtti -Wvla -D__MBED__=1 -DDEVICE_I2CSLAVE=1 -DTARGET_LIKE_MBED -DDEVICE_PORTINOUT=1 -D__MBED_CMSIS_RTOS_CM -DTARGET_STM32F1 -DDEVICE_RTC=1 -DTOOLCHAIN_object -DDEVICE_SERIAL_ASYNCH=1 -D__CMSIS_RTOS -DMBED_BUILD_TIMESTAMP=1483112652.49 -DTOOLCHAIN_GCC -DDEVICE_CAN=1 -DTARGET_LIKE_CORTEX_M3 -DTARGET_CORTEX_M -DARM_MATH_CM3 -DTARGET_UVISOR_UNSUPPORTED -DTARGET_M3 -DDEVICE_SERIAL=1 -DDEVICE_INTERRUPTIN=1 -DDEVICE_I2C=1 -DDEVICE_PORTOUT=1 -D__CORTEX_M3 -DDEVICE_STDIO_MESSAGES=1 -DTARGET_FF_MORPHO -DTARGET_FF_ARDUINO -DTARGET_RELEASE -DTARGET_STM -DTOOLCHAIN_GCC_ARM -DDEVICE_PORTIN=1 -DDEVICE_SLEEP=1 -DTARGET_NUCLEO_F103RB -DDEVICE_SPI=1 -DDEVICE_SPISLAVE=1 -DDEVICE_ANALOGIN=1 -DDEVICE_PWMOUT=1 -DTARGET_STM32F103RB -include mbed_config.h -MMD -MP
+CC_FLAGS = -c -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fmessage-length=0 -fno-exceptions -fno-builtin -ffunction-sections -fdata-sections -funsigned-char -MMD -fno-delete-null-pointer-checks -fomit-frame-pointer -mcpu=cortex-m3 -mthumb -Os -std=gnu99 -D__MBED__=1 -DDEVICE_I2CSLAVE=1 -DTARGET_LIKE_MBED -DDEVICE_PORTINOUT=1 -D__MBED_CMSIS_RTOS_CM -DTARGET_STM32F1 -DDEVICE_RTC=1 -DTOOLCHAIN_object -DDEVICE_SERIAL_ASYNCH=0 -D__CMSIS_RTOS -DMBED_BUILD_TIMESTAMP=1483112652.49 -DTOOLCHAIN_GCC -DDEVICE_CAN=0 -DTARGET_LIKE_CORTEX_M3 -DTARGET_CORTEX_M -DARM_MATH_CM3 -DTARGET_UVISOR_UNSUPPORTED -DTARGET_M3 -DDEVICE_SERIAL=1 -DDEVICE_INTERRUPTIN=1 -DDEVICE_I2C=1 -DDEVICE_PORTOUT=1 -D__CORTEX_M3 -DDEVICE_STDIO_MESSAGES=1 -DTARGET_FF_MORPHO -DTARGET_FF_ARDUINO -DTARGET_RELEASE -DTARGET_STM -DTOOLCHAIN_GCC_ARM -DDEVICE_PORTIN=1 -DDEVICE_SLEEP=1 -DTARGET_NUCLEO_F103RB -DDEVICE_SPI=1 -DDEVICE_SPISLAVE=1 -DDEVICE_ANALOGIN=1 -DDEVICE_PWMOUT=1 -DTARGET_STM32F103RB -include mbed_config.h -MMD -MP
+CPPC_FLAGS = -c -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fmessage-length=0 -fno-exceptions -fno-builtin -ffunction-sections -fdata-sections -funsigned-char -MMD -fno-delete-null-pointer-checks -fomit-frame-pointer -mcpu=cortex-m3 -mthumb -Os -std=gnu++98 -fno-rtti -Wvla -D__MBED__=1 -DDEVICE_I2CSLAVE=1 -DTARGET_LIKE_MBED -DDEVICE_PORTINOUT=1 -D__MBED_CMSIS_RTOS_CM -DTARGET_STM32F1 -DDEVICE_RTC=1 -DTOOLCHAIN_object -DDEVICE_SERIAL_ASYNCH=0 -D__CMSIS_RTOS -DMBED_BUILD_TIMESTAMP=1483112652.49 -DTOOLCHAIN_GCC -DDEVICE_CAN=0 -DTARGET_LIKE_CORTEX_M3 -DTARGET_CORTEX_M -DARM_MATH_CM3 -DTARGET_UVISOR_UNSUPPORTED -DTARGET_M3 -DDEVICE_SERIAL=1 -DDEVICE_INTERRUPTIN=1 -DDEVICE_I2C=1 -DDEVICE_PORTOUT=1 -D__CORTEX_M3 -DDEVICE_STDIO_MESSAGES=1 -DTARGET_FF_MORPHO -DTARGET_FF_ARDUINO -DTARGET_RELEASE -DTARGET_STM -DTOOLCHAIN_GCC_ARM -DDEVICE_PORTIN=1 -DDEVICE_SLEEP=1 -DTARGET_NUCLEO_F103RB -DDEVICE_SPI=1 -DDEVICE_SPISLAVE=1 -DDEVICE_ANALOGIN=1 -DDEVICE_PWMOUT=1 -DTARGET_STM32F103RB -include mbed_config.h -MMD -MP
 ASM_FLAGS = -x assembler-with-cpp -D__CMSIS_RTOS -D__MBED_CMSIS_RTOS_CM -D__CORTEX_M3 -DARM_MATH_CM3 -c -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fmessage-length=0 -fno-exceptions -fno-builtin -ffunction-sections -fdata-sections -funsigned-char -MMD -fno-delete-null-pointer-checks -fomit-frame-pointer -mcpu=cortex-m3 -mthumb -Os
-CC_SYMBOLS = -D__MBED__=1 -DDEVICE_I2CSLAVE=1 -DTARGET_LIKE_MBED -DDEVICE_PORTINOUT=1 -D__MBED_CMSIS_RTOS_CM -DTARGET_STM32F1 -DDEVICE_RTC=1 -DTOOLCHAIN_object -DDEVICE_SERIAL_ASYNCH=1 -D__CMSIS_RTOS -DMBED_BUILD_TIMESTAMP=1483112652.49 -DTOOLCHAIN_GCC -DDEVICE_CAN=1 -DTARGET_LIKE_CORTEX_M3 -DTARGET_CORTEX_M -DARM_MATH_CM3 -DTARGET_UVISOR_UNSUPPORTED -DTARGET_M3 -DDEVICE_SERIAL=1 -DDEVICE_INTERRUPTIN=1 -DDEVICE_I2C=1 -DDEVICE_PORTOUT=1 -D__CORTEX_M3 -DDEVICE_STDIO_MESSAGES=1 -DTARGET_FF_MORPHO -DTARGET_FF_ARDUINO -DTARGET_RELEASE -DTARGET_STM -DTOOLCHAIN_GCC_ARM -DDEVICE_PORTIN=1 -DDEVICE_SLEEP=1 -DTARGET_NUCLEO_F103RB -DDEVICE_SPI=1 -DDEVICE_SPISLAVE=1 -DDEVICE_ANALOGIN=1 -DDEVICE_PWMOUT=1 -DTARGET_STM32F103RB 
+CC_SYMBOLS = -D__MBED__=1 -DDEVICE_I2CSLAVE=1 -DTARGET_LIKE_MBED -DDEVICE_PORTINOUT=1 -D__MBED_CMSIS_RTOS_CM -DTARGET_STM32F1 -DDEVICE_RTC=1 -DTOOLCHAIN_object -DDEVICE_SERIAL_ASYNCH=0 -D__CMSIS_RTOS -DMBED_BUILD_TIMESTAMP=1483112652.49 -DTOOLCHAIN_GCC -DDEVICE_CAN=0 -DTARGET_LIKE_CORTEX_M3 -DTARGET_CORTEX_M -DARM_MATH_CM3 -DTARGET_UVISOR_UNSUPPORTED -DTARGET_M3 -DDEVICE_SERIAL=1 -DDEVICE_INTERRUPTIN=1 -DDEVICE_I2C=1 -DDEVICE_PORTOUT=1 -D__CORTEX_M3 -DDEVICE_STDIO_MESSAGES=1 -DTARGET_FF_MORPHO -DTARGET_FF_ARDUINO -DTARGET_RELEASE -DTARGET_STM -DTOOLCHAIN_GCC_ARM -DDEVICE_PORTIN=1 -DDEVICE_SLEEP=1 -DTARGET_NUCLEO_F103RB -DDEVICE_SPI=1 -DDEVICE_SPISLAVE=1 -DDEVICE_ANALOGIN=1 -DDEVICE_PWMOUT=1 -DTARGET_STM32F103RB 
 
-LD_FLAGS =-Wl,--gc-sections -Wl,--wrap,main -Wl,--wrap,_malloc_r -Wl,--wrap,_free_r -Wl,--wrap,_realloc_r -Wl,--wrap,_calloc_r -mcpu=cortex-m3 -mthumb 
+LD_FLAGS =-Wl,--gc-sections -mcpu=cortex-m3 -mthumb 
 LD_SYS_LIBS = -lstdc++ -lsupc++ -lm -lc -lgcc -lnosys
 
+DEBUG=1
 
 ifeq ($(DEBUG), 1)
-  CC_FLAGS += -DDEBUG -O0
+  CC_FLAGS += -DDEBUG -O0 -g3
 else
-  CC_FLAGS += -DNDEBUG -Os
+  CC_FLAGS += -DNDEBUG -Os -g3
 endif
 
 
diff --git a/main.cpp b/main.cpp
index ccb0f20..2cea268 100644
--- a/main.cpp
+++ b/main.cpp
@@ -6,9 +6,8 @@ int main() {
     
     while(1) {
         myled = 1;
-        wait(0.5);
+        for (int i = 0; i < 100000000; i++);
         myled = 0;
-        wait(0.5);
+        for (int i = 0; i < 100000000; i++);
     }
 }

diff --git a/mbed-src/targets/cmsis/TARGET_STM/TARGET_STM32F1/TARGET_NUCLEO_F103RB/system_stm32f1xx.c b/mbed-src/targets/cmsis/TARGET_STM/TARGET_STM32F1/TARGET_NUCLEO_F103RB/system_stm32f1xx.c
index 196ddff..65ff176 100644
--- a/mbed-src/targets/cmsis/TARGET_STM/TARGET_STM32F1/TARGET_NUCLEO_F103RB/system_stm32f1xx.c
+++ b/mbed-src/targets/cmsis/TARGET_STM/TARGET_STM32F1/TARGET_NUCLEO_F103RB/system_stm32f1xx.c
@@ -141,8 +141,8 @@
   */
 
 /* Select the clock sources (other than HSI) to start with (0=OFF, 1=ON) */
-#define USE_PLL_HSE_EXTC (1) /* Use external clock */
-#define USE_PLL_HSE_XTAL (1) /* Use external xtal */
+#define USE_PLL_HSE_EXTC (0) /* Use external clock */
+#define USE_PLL_HSE_XTAL (0) /* Use external xtal */
 
 /**
   * @}

動かしてみる

インストールしたQEMUを用いて、Lチカのプログラムを動かしてみたいと思います。

コマンドラインからQEMUを動かす方法は以下に書かれています。

gnuarmeclipse.github.io

QEMU実行コマンドのテンプレートは以下になります。

qemu-system-gnuarmeclipse --verbose --verbose --board ボード名 \
--mcu マイコン名 -d unimp,guest_errors --gdb tcp::1234 \
--nographic --image プログラム名.elf \
--semihosting-config enable=on,target=native \
--semihosting-cmdline プログラム名

今回の対象ボードはNUCLEO-F103RBなので、ボード名にはNUCLEO-F103RB、マイコン名にはSTM32F103RBを設定します。 プログラム名はmbed_blinkyなので、実行するコマンドは以下になります。

$ /opt/gnuarmeclipse/qemu/bin/qemu-system-gnuarmeclipse \
--verbose --verbose --board NUCLEO-F103RB --gdb tcp::1234 \
--mcu STM32F103RB -d unimp,guest_errors  \
--image mbed_blinky.elf \
--semihosting-config enable=on,target=native \
--semihosting-cmdline mbed_blinky

このコマンドはmbed_blinky.elfのあるディレクトリで行う必要があるため、 mbedからエクスポートしてきたプログラムをmakeした後、 mbed_blinky.elfの生成されている.buildディレクトリに移動してQEMUを起動します。

$ cd .build
$ /opt/gnuarmeclipse/qemu/bin/qemu-system-gnuarmeclipse \
--verbose --verbose --board NUCLEO-F103RB --gdb tcp::1234 \
--mcu STM32F103RB -d unimp,guest_errors  \
--image mbed_blinky.elf \
--semihosting-config enable=on,target=native \
--semihosting-cmdline mbed_blinky

これで正しく動いていればNUCLEOの画像が表示されLEDの部分が点滅するはずです。

とりあえず今日はここまで。

おまけ: デバッガを繋いでみる

QEMU起動時に--gdb tcp::1234オプションをつけているので、TCP1234ポートにgdbでアタッチしてデバッグができます。

まず以下のコマンドでgdbを起動します。

$ arm-none-eabi-gdb mbed_blinky.elf

gdbが起動したら、QEMUgdbサーバーに接続します。

(gdb) tar remote :1234

後は普通にデバッグするだけです。

おまけ: その他情報

LPC1768サポートを追加した方がいらっしゃるようです(当方未調査)。

myokota.hatenablog.jp

QEMUのFeaturesリストを見るとCortex-M0サポートなども予定してるみたいです。

Features/QOM/CPU - QEMU