CORTEX-M3のリセットハンドラとTOPPERS/ASPの実装

先日、TOPPERS/ASPCORTEX-M3のリセットハンドラの実装に関して

結局、プログラム自身での設定とベクトルでの設定を切り替える用にしなければなりません。TOPPERS/ASPCORTEX-M3依存部は切り替え式ですが、その場合のスタック・ポインたは例外ベクトルから読んでいます。だめじゃんそれ!

と書きました。なぜ駄目なのか、自分へのメモをかねて書き留めておきます。

最初に、CORTEX-M3のリセット後の振る舞い

LPC1763で使用しているARM CORTEX-M3コアは、リセット直後に、メモリから次の二つのレジスタに値をロードします。

  • PC : Program Counter
  • MSP : Master Stack Pointer

0番地からMSP、4番地からPCをプロセッサはロードします。
PCは言わずとしれた実行アドレスのポインタです。プロセッサはこの二つのレジスタへのロードが完了すると、即座にPCにロードされたアドレスへジャンプします。
MSPはリセットや例外処理時に使うスタックのトップを示すポインタです。
多くのプロセッサはリセット時にはMSPをロードしません。リセット直後にジャンプしていった先で好きな値をロードすればいいからです。CORTEX-M3がこの値をわざわざロードするのは、リセット直後のジャンプ先、つまり先のリセット時にロードしたアドレスを、C言語のサブルーチンとして指定することができるからです。
これまでのプロセッサの場合、リセット時のジャンプ先で何とかしてスタック・ポインタを設定しなければなりませんが、それをC言語のサブルーチン内部で行うと、サブルーチンへのエントリーコードが暴走してしまいます。なぜならばサブルーチンのエントリーコードはスタックを使いますが、この時点では未設定だからです。
CORTEX-M3はリセット時にMSPに値をロードするため安全にC言語関数をリセット直後から実行できます。そのはずです。しかしここに一つ落とし穴があります。それを説明するにはLPC1768はSTM32F103のメモリ構成を説明しなければなりません。

リセット後のメモリ構成

LPC1768やSTM32F103では、リセット直後0番地にFlash ROM*1を割り当てます。これは当然のことです。CORTEX-M3はリセット時にPCとMSPを0番地からロードしますから、ここにSRAMを置くわけにはいきません。そんなことをすると未初期化変数の値をPCとMSPにロードしてしまい、即座にプロセッサが暴走します。ですから、リセット直後の0番地はかならずFlash ROMでなければなりません。
LPC1768はリセット時に内蔵ブートROM0番地にマッピングされています。STM32F103はブート時のメモリ種類をブートピンの設定で決定します。これによってリセット直後にCORTEX-M3はMSPの値を正しく読み込むことができます。

デバッガはどうする

上のような仕掛けでCORTEX-M3はリセット直後にMSPの値を正しく読み込むことができます。しかしこれはリセットからCPUが自動的にブートする場合の話です。問題はデバッガを使う場合です。
デバッガでプログラムをSRAM領域にロードしてデバッグする場合を考えます。この場合、通常のデバッガはMSPを設定してくれません。一方で、プロセッサコアもMSPを設定しませんので、何も考えずに実行すると暴走しています。
デバッガにプログラムをロードするたびに手動でMSPを設定する方法もありますが、これはうっかり忘れた場合の結果がひどいために、あまり使いたくありません。結局プログラムが自分自身でMSPを設定するほうがいいということになります。CORTEX-M3の設計者はあまり深く考えずにこの機能を実装したのかもしれません。

ASPCORTEX-M3依存部の方法

さて、ようやくCORTEX-M3依存部のやり方についてお話しできます。cortex-m3依存部のstart.Sのリセット・エントリは次のようになっています。

	.text
	.align 2
	.code 16
	.syntax unified
	.globl _start
	.type _start, function
_start:
	cpsid f                 /* 割込みロック状態へ */

#ifdef INIT_MSP
	/*
	 * MSPの設定
	 */
	mov   r0, #CONTROL_MSP  /* MSPを有効に  */
	msr   control, r0
	isb                     /* control の操作後に必要 */
	ldr  r0,=_kernel_istkpt
	ldr  r1,[r0]
	msr  msp, r1
#endif /* INIT_MSP */

#ifdef INIT_MSPで条件アセンブルされる部分が、MSPをロードするコードです。このコードは_kernel_istkptが示すアドレスからMSPの値をロードします。そして、cortex-m3依存部では、_kernel_istkptを0と決め打ちしています。
STM32F103ならばこれでかまいません。このプロセッサはメモリのリマッピング機能があるため、RAMを0番地にリマップできます。しかし、LPC1768にはその機能がないため、何とかしなければなりません。この部分はROM化コードの構成にも関わるところなので、うっかり安易な手出しができないところです。
少し考えてみます。

*1:あるいはブート用のROM