スタック

スタック

2つのスタックポインタ

通常のマイコンにはスタックポインタは一つしかありません。しかしCortex-M3はスタックポインタ(SP)を2つ持っています。汎用レジスタの13番がSPになっています。R13(SP)は、メインスタック(SP_main) とプロセススタック(SP_process)との間で切り替わるバンクレジスタの構成になっています。

ユーザーからは、SP_processまたはSP_mainのいずれか一方のスタックのみが、R13として見ることができます。

ハンドラモードでは常時SP_mainが使用されます。スレッドモードではSP_mainまたはSP_processのどちらでも使用できます。スレッドモードでは、MSR命令を使用してCONTROL[1]に書き込むことにより、メインスタックからプロセススタックに切り替えが可能であり、さらにハンドラモードから退出時にEXC_RETURN値を使用して選択も可能です。

通常、例外(割り込みやフォールト)が起きる前は、スレッドモードで命令を実行しています。この時使うスタックをユーザーが選択できるということです。しかし、一旦、例外(割り込みやフォールト)が起きて、ハンドラモードになるとCortex-M3はSP_mainだけを使うようになります。例えば、割り込みのサービスルーチンを実行中に、さらに割り込みが発生し、割り込みのネスティングが行われる場合は、必ずSP_mainが使われます。

他のマイコンの様に、スタックポインタは、SP_mainの一つだけでもまったく問題はありません。しかし、スレッドモード時はSP_process、ハンドラモードの時はSP_mainを使うようにしておくと、OS等を使った場合に、ユーザープログラムの暴走でOSのスタックの値を壊されることから守ることが出来ます。

SP_mainの初期値はベクタテーブルで指定

図1

通常のマイコンでは、スタックポインタの値は、プログラムコードの中の初期化ルーチンの中に記述され、コードが実行されることで設定されます。しかし、Cortex-M3では、SP_mainの初期値はベクタテーブルの最も若いアドレスに記述するようになっています。

通常のマイコンのベクタテーブルの一番若いアドレスにはリセットスタートアドレスの値が入っていますが、Cortex-M3では、SP_mainの初期値の次がリセットスタートアドレスですので、注意が必要です。これはプログラムコードの中の初期化ルーチンの中で定義しなくてすむので、ユーザーにとっては、便利な方式です。

スタッキング

Cortex-M3では、例外処理が発生すると、ハードウェアにより自動的にMCU状態(コンテキスト)のスタッキングが行われます。この時スタックされるコンテキストは、リンクレジスタ(R14)とプログラムカウンタPC(R15)とプログラムステータス レジスタ(xPSR)と汎用レジスタの一部です。Cortex-M3は汎用レジスタを13本(R0~R12)持っています。その内スタックされるレジスタは、R0~R3とR12です。R4~R11はスタックされませんので注意が必要です。

図2

ARMアーキテクチャには、アーキテクチャプロシージャ呼び出し規格(AAPCS: ARM Architecture Procedure Call Standard)があり、この規格に準拠してスタックされます。スタックにPUSHする順は以下になります。この時、SPはビット[1:0] への書き込みを無視するため、4バイト境界のワードに自動的に揃えられます。PUSHされる順とスタックの内容(位置関係)が異なりますので、注意が必要です。

  • ●プログラムカウンタ(PC)
  • ●プログラムステータスレジスタ(xPSR)
  • ●R0~R3
  • ●R12
  • ●リンクレジスタ(LR)

スタックキングの後

割り込みが受け付けられて、スタッキングとベクタフェッチは同時に行われます。その後、割り込みサービスルーチンが始まります。この時にレジスタが更新されますので、簡単に説明します。

まずスタックポインタR13( SP_mainかSP_process)はスタッキングの最中に新しいアドレスに更新されますが、割り込みサービスルーチンが始まると、メインスタック(SP_main)が使われるようになります。これは、Cortex-M3がハンドラモードに移ったことを意味します。

PC(R15)は、ベクタのフェッチが終わり、ハンドラによって、ベクタアドレスに変わります。そのベクタアドレスから命令をフェッチします。xPSRの中の割り込みPSRフィールド(ISR番号)は、現在アクティブになっている例外の割り込み処理ルーチン(ISR)番号を示しますので、新しい割り込みサービスルーチンに移ると、新しい例外の番号に変わります。LR(R14)はEXC_RETURNという、例外から復帰するときに使われる特別な値が入ります。

この他、統合ネスト型ベクタ割り込みコントローラ(NVIC)のレジスタも割り込みの種類によって更新されます。(レジスタの詳細は割り込みのところで説明します。)

例外からの退出

例外の処理が終るとハンドラによって、スレッドモードに戻ります。その手順はスタックの逆の手順で行われますが、簡単に説明すると以下のようになります。

まずは、レジスタのポップが行われます。スタックからPC、xPSR、R0、R1、R2、R3、R12、LRをポップします。ポップの順はスタッキングと同じです。SPも復元されます。割り込みがネストしていれば、別の例外へ遷移しますが、その時のSPはメインスタック(SP_main)のままです。 スレッドモードへ復帰する場合、SPは割り込みに入る前に選択されていたメインスタック(SP_main) またはプロセススタック(SP_process)です。そして、統合ネスト型ベクタ割り込みコントローラ(NVIC)のレジスタも復元されます。

割り込み処理の状態遷移の詳細は、「第13回:メモリフォーマット、データタイプ、例外/割り込み処理」で説明します。

図3