ラズパイ3でベアメタル - QEMUでUART(PL011)
「キー入力と文字の画面表示」くらいはRaspberry Pi 3実機を使わずに、
QEMUで動作確認できると便利です。
現状のQEMU(Debian JessieでAPTでインストールできるもの)でも、
UARTでPL011(*1)が利用可能です。
(*1): PrimeCell UART(PL011) Technical Reference Manual
今回は、PL011 UART向けにこれまでのUARTのプログラムを書き換え、
QEMU上で動作確認してみます。
なお、Raspberry Pi 3で64bitベアメタル(bare metal)プログラミングを試してみる
本シリーズの目次はコチラです。
1. Mini UARTからの変更点
Mini UARTからの変更点は主に以下の点です。
1. レジスタ名とアドレス
2. FIFOステータスを示すレジスタのビットの意味の読み替え
「送受信のFIFOがあり、
FIFOステータスのレジスタを確認してから、
FIFOへアクセスする」という
UARTの基本的な点は(当たり前かも知れませんが、)変わりません。
3. ソースコード
これまで作成した以下の2つのUARTプログラムをQEMU向けに変更します。
なお、作成したプログラムはMakefile付きで以下のGitHubリポジトリの、
それぞれ以下のディレクトリへ追加しています。
3.0. start.Sの変更
その3と4のプログラム両方に共通する変更点として、プログラムの実行開始アドレスの違いがあります。
QEMUでは実行開始アドレスは0x40080000なので、
スタック領域も0x40080000より上(アドレスが小さくなる方向)の領域を使用するよう、
スタックポインタ(sp)に0x40080000を設定します。
mov w0, #0x40080000 /* 汎用32ビットレジスタw0へ0x40080000を格納 */ mov wsp, w0 /* w0をspの下位32ビット(wsp)へ格納 */ bl main /* mainへ分岐 */
また、ビルド時のldコマンドで指定する-Ttextオプション(textセクション開始アドレス)も0x40080000へ変更します。
改めてまとめると以下のとおりです。
(今回も依然としてtextセクションしか使用しないプログラムなので、リンカスクリプトはサボり、
ldコマンドでtextセクションのみ指定しています。)
[PC]$ aarch64-linux-gnu-as -o start.o start.S [PC]$ aarch64-linux-gnu-gcc -c -o loop.o loop.c [PC]$ aarch64-linux-gnu-ld -Ttext 0x40080000 -o kernel8.elf start.o loop.o [PC]$ aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img
なお、上述のGitHubリポジトリには、これらのコマンドを記述したMakefileを追加しているので、
それぞれのプログラムのディレクトリへ移動し、makeを実行するだけでビルドできます。
3.1. 'A'を送信し続けるプログラム
変更点はレジスタとビットフィールドの定数のみです。
#define PL011_UARTDR (*(volatile unsigned int *)0x09000000) #define PL011_UARTFR (*(volatile unsigned int *)0x09000018) #define PL011_UARTFR_TXFF (1U << 5) int main(void) { while (1) { while (PL011_UARTFR & PL011_UARTFR_TXFF); PL011_UARTDR = (unsigned int)'A'; } return 0; }
3.2. エコーバックプログラム
こちらも同じく、変更点はレジスタとビットフィールドの定数のみです。
#define PL011_UARTDR (*(volatile unsigned int *)0x09000000) #define PL011_UARTFR (*(volatile unsigned int *)0x09000018) #define PL011_UARTFR_TXFF (1U << 5) #define PL011_UARTFR_RXFE (1U << 4) int main(void) { volatile char ch; while (1) { while (PL011_UARTFR & PL011_UARTFR_RXFE); ch = (char)PL011_UARTDR; while (PL011_UARTFR & PL011_UARTFR_TXFF); PL011_UARTDR = (unsigned int)ch; } return 0; }
4. 動作確認
上述のビルド方法でビルドし、できあがったkernel8.imgは、
以下のコマンドで、QEMU上で実行できます。
[PC]$ qemu-system-aarch64 -cpu cortex-a57 -M virt -kernel kernel8.img
QEMU起動後、「Ctrl+Alt+2」でシリアルポートの画面へ切り替えられます。
なお、起動直後の"(qemu)"のプロンプトが表示された状態は、
「QEMUモニター」というQEMUのモードで、シリアルポート画面からは「Ctrl+Alt+1」で切り替えられます。
このモードでは、汎用レジスタの値や、任意のアドレス上の値を表示できたり、デバッグするのにとても便利です。
詳細は以下のマニュアルなどが参考になります。