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の初期値はベクタテーブルで指定
通常のマイコンでは、スタックポインタの値は、プログラムコードの中の初期化ルーチンの中に記述され、コードが実行されることで設定されます。しかし、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はスタックされませんので注意が必要です。
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回:エディアン、メモリフォーマット、データタイプ、例外/割り込み処理」で説明します。
こちらも是非
“もっと見る” Cortex-M編
SysTick、電力管理
SysTick機能を有効にするには、SysTick制御およびステータスレジスタを使用します。このレジスタのENABLEビットを1にすると、カウンタは動作をはじめます。つまり、カウンタにリロード値がロードされてから、カウントダウンが開始されます。
メモリマップ
Cortex-M3のメモリマップには、一般的なマイコンのメモリマップと若干異なる特徴があります。一般的なマイコンでは、メモリ領域を変更できるものもあります。しかし、Cortex-M3のメモリマップは定義されたメモリマップになっており、アドレス領域のマッピングは固定です。
ベクタテーブル
Cortex-M3のベクタテーブルは0番地から始まります。一般的なマイコンは、ベクタテーブルの最小アドレス部(0番地)にはリセットベクタが割り当てられていますが、Cortex-M3ではメインスタック(SP_main)の初期値が割り当てられています。