ArduinoUnoのブートシーケンス


概要

ArduinoでOSを作るために,ブートローダやメモリ配置,割り込みベクタについて調べたことをまとめておく.ここでは,特に断らない限りArduino Unoについて書いていく

ブートローダの振る舞い

Arduinoのブートローダは,ユーザが実装するアプリケーションとは別に存在し,工場出荷時に予めチップ(e.g., atmega328p)に組み込まれている.電源が入る,またはリセットボタンが押されるとこのブートローダが起動し,ブートローダはシリアルを監視する.PCからシリアル経由でプログラムを受け取ると,プログラムの配置情報(リンカスクリプト)に従ってフラッシュに書き込む.その後,0x00番地にジャンプし,アプリケーションを起動する.
一方,ブートローダは一定時間待ってもプログラムが送られてこないと,0x00番地にジャンプして書き込み済みのアプリケーションを起動する.

ブートローダの書き込み位置

Arduino Unoのatmeta328pは,16KBのフラッシュメモリと2KBのSRAMを備えている.読み込むだけのプログラムはフラッシュメモリに格納され,読み書きが必要なデータがSRAMに置かれる.フラッシュメモリには,プログラム(ユーザが作成)と,プログラムをロードするブートローダが書き込まれ,どの位置にブートローダを書き込むか(Boot Flash Section)は,ヒューズビットによって変更することができる.
さらに,ヒューズビットは3種類(EFUSE, HFUSE, LFUSE)に分類され,Boot Flash Sectionの位置を決めるのはHFUSEの下位3ビット目,下位2ビット目に当たる,BOOTSZ1, BOOTSZ0であり,それぞれでデフォルト値は(0, 0)である(データシートp349).これらビットの組み合わせとBoot Flash Sectionのアドレスの対応を下にまとめる

(BOOTSZ1, BOOTSZ0) Boot Flash Sectionアドレス
(1, 1) 0x3f00
(1, 0) 0x3e00
(0, 1) 0x3c00
(0, 0) 0c3800

ここで,デフォルト値が(0, 0)であることから,Boot Flash Sectionのアドレスは0x3800であることがわかる.
ただし,atmega328pは,データシートによるとすべての命令は2倍とで構成される,とあるため実際のアドレスを考えるときには2倍して考えないといけない.よって,書き直すと以下のようになる.

(BOOTSZ1, BOOTSZ0) Boot Flash Sectionアドレス
(1, 1) 0x7e00
(1, 0) 0x7c00
(0, 1) 0x7800
(0, 0) 0c7000

よって,開始位置は0x7000である.

リセットベクタ/割り込みベクタ

リセットベクタとは,電源が入ったとき,もしくはリセットボタンが押された時にMCUが実行を開始するアドレスのことを指す.また,割り込みベクタとは,各種割り込みが入ったときの割り込みベクタを配置するべき開始アドレスを指す.リセットベクタと割り込みベクタはあるビットを操作することによって変更可能になっている.リセットベクタの位置を決めるのはBOOTRSTで,これは前述のHFUSEの最下位ビットに相当する.また,割り込みベクタの位置を決めるのはIVSELで,これはMCUCRレジスタの下位2ビット目のことを指す.
BOOTRSTが1の場合,リセットベクタは0x00に配置され,0の場合Boot Flash Addressの先頭番地に配置される.電源が入ったとき,もしくはリセットボタンが押されたときには常にブートローダから起動してほしいので,BOOTRSTは0で有ることが望ましい(ただし,BOOTRSTのデフォルト値は1).一方,IVSELが0のとき,リセットベクタは0x002に配置され,1のときにはリセットアドレス+0x0002に配置される.つまり,0のときは固定で0x002番地,1のときにはリセットのアドレスに続くアドレスに配置される(1命令が2バイトで構成されるため,続くアドレスとは2バイトずれる).
よって,ブートローダが起動しているときには,割り込みベクタはブートローダが用意した割り込みベクタを使えたほうが都合が良いので,IVSELの値は1のほうが良い(そして恐らく,リセットされるとハードウェアでIVSELは強制的に1になる).
一方,アプリケーション(e.g., OS)は0番地から配置され,起動したあとは独自の割り込みハンドラを設定したい.そのため,アプリケーション実行直後の初期化処理などで,IVSELを0にし,割り込みベクタの位置を0x002にする.

Arduino Unoのブートローダ設定

Arduinoのブートローダは,0x7800に書き込まれている.これは,Arduino IDEをインストールした時に一緒に入る,"/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/bootloaders/atmega/Makefile"を見ると,以下の行によって確認できる.

atmega328: LDSECTION  = --section-start=.text=0x7800

しかし,一方でブートローダの書き込み位置を決めるBOOTSZ1, BOOTSZ0のでデフォルト値は0, 0であった.この場合,Boot Flash Secitonのアドレスは0x7000で,仮にBOOTRSTの値が0でも辻褄が合わなくなる.
そこで,このMakefileを見ると,以下の設定が見つかる.

atmega328_isp: HFUSE = DA
atmega328_isp: LFUSE = FF
atmega328_isp: EFUSE = 05

このように,HFUSE = DAとなっているDA = 1101 1010なので,下位3ビットを見ると(0, 1, 0)となる,つまり,(BOOTSZ1, BOOTSZ0) = (0, 1)となるので,Boot Flash Secitonのアドレスは0x7800で,BOOTRSTも0である.つまり,ブートローダを書き込む時にヒューズビットも変更することで,この動作を実現している.