読者です 読者をやめる 読者になる 読者になる

割り込みを体験する #3(完)

これまでで当初の目標であった割り込み確認のための処理は全て取り上げました。
今回は、動作確認まで行いたいと思います。


コンパイル・リンク

以下のコマンドラインコンパイル、リンクを行います。

gcc  -I. -O2 -Wall -nostdinc -ffreestanding -march=armv4 -mabi=apcs-gnu -c -o timer.o timer.c
gcc  -I. -O2 -Wall -nostdinc -ffreestanding -march=armv4 -mabi=apcs-gnu -c -o main.o main.c
gcc  -I. -c -o start.o start.S
ld -static -T u-boot.lds  -o u-boot.bin start.o timer.o main.o
objcopy -O binary u-boot.bin

指定しているオプションの内、特記すべきものの概要は以下の通りです。

Option 概要
-nostdinc glibc標準Includeパスを通さないようにする
-ffreestanding 自立環境(標準ライブラリが存在しない、`main'関数から始まるとは限らない)事を意味する(-fno-builtinも暗黙で指定される)
(-fno-builtin) gccのbuiltin関数を利用しない
-march アーキテクチャ指定。ARMv4である事を指示
-mabi ABIの種類の指定
-static 共有ライブラリをリンクしない
-T リンカスクリプト指定

ABIとは簡単に言ってしまうと、サブルーチンを呼び出す際、引数はどうやって渡すのか(呼出元/先でレジスタをどう使う?スタックはどう使う?)など、低レベルのインターフェイスの規定です。
ARMではこのABIに以下の指定が可能です。

  • apcs-gnu*1
    • 従来のABIで、obsoleteとされています。
  • aapcs-linux*2
    • Embedded ABIと呼ばれ、組込みアプリケーション向けとしているようです

この2つの違いは構造体のアライメントに違いがあるようです。
(前者のほうが無条件に4Byteでパッキングされる?)


次に、リンカスクリプトを確認します。
リンカスクリプトとは、リンカ(ld)に指定する、オブジェクトの間取り図のようなものです。
スクリプト内の一部の定義はオプションでも指定可能ですが、細かく定義する場合は以下のようなスクリプトを書きます。

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(start)

MEMORY
{
       ROM (rx): ORIGIN = 0x00000000, LENGTH = 256k
       RAM (rw): ORIGIN = 0x30000000, LENGTH = 32M
     STACK (rw): ORIGIN = 0x33FFFFF0, LENGTH = 128k
}
SECTIONS
{
  . = 0x00000000;
  . = ALIGN(4);
  .text :
  {
    start.o(.text)
    *(.text)
    __etext = .;
  } > ROM
  . = ALIGN(4);
  .data :
  {
    __init_values_start = .;
    *(.data)
    __init_values_end = .;
  } > RAM AT>ROM
  . = ALIGN(4);
  .bss :
  {
    __bss_start = .;
    *(.bss)
    *(COMMON)
    . = ALIGN(4);
    __bss_end = .;
  } > RAM
  .stack :
  {
    *(.stack)
  	. = ALIGN(4);
    __init_stack = .;
  } > STACK
}

内容を説明します:

  • オブジェクトのフォーマットやプログラムのエントリの指定
    • ARMでリトルエンディアンである事を指定しています。
    • startシンボルが開始アドレスである事を指定しています。
  • メモリ配置指定
    • Bishopのアドレスマップから決定しています。
    • dataセクション用のRAM領域のサイズは32MBとし、stackセクション用の領域は(大きすぎますが)128KBとし、64MB先の位置からとしました。
  • セクション配置指定
    • textセクション先頭にstart.oのtextセクションを配置します。ベクタテーブルが0番地を前提としているためです。

やっと実行する準備が整いました(長かった…)。

実行!

それではこれまでのプログラムを実行してみます。
コマンドラインは以下の通りです。うまくすれば、'.'が1秒間隔で表示し続けるはずです。
(生成したu-boot.binは、以下のコマンド実行時のディレクトリにある必要があります)

qemu-system-arm  -M pe201b  -mtdblock null.fs -nographic

-nographicは、コンソールアプリケーションとしてqemuを実行するためのoptionです。(-serial stdioも明示的に指定されます)
そして無事に、以下のような出力がなされました。

m......

'm'はmain関数のものです。'.'は約1秒毎に表示されていきました。

まとめ

長くなってしまいましたが、今回は割り込みとタイマに関して確認してみました。
内容がないようなだけに、割り込み処理の流れ自体は面白みがありませんでしたが、
割り込み処理の最低限の動作に関しては触れられたと思います。


また今回では取り上げませんでしたが、割り込み処理中に別の割り込みが発生したらどうすべきか、等、割り込み処理において他に考慮すべき点が多々あります。


記事のまとめ方と併せて今後の課題にしたいと思います。

*1:apcs:ARM Procedure Call Standard

*2:aapcs:Procedure Call Standard for the ARM Architecture