プロセッサモード

プロセッサモードとは

Cortex-Aプロセッサを初めとする、ARMプロセッサ(Cortex-Mシリーズを除く)は7つのプロセッサモードを持っています。例外が発生することで、プロセッサモードが変わり、自動的に一部のレジスタが切り替わります。

FIQモードは、r8からr14が専用レジスタになり、他のモードはr13とr14が専用レジスタとなり、cpsr(第4回参照)の内容は、各モードのspsr(保存プログラムステータスレジスタ)に保存されます。

プロセッサモードが変化することでスタックポインタr13が自動的に切り替わりますので、プログラムにて使用するモードのスタックポインタを、割り込み禁止状態での初期化が必要です。

【プロセッサモード一覧表】
プロセッサモード 内容 動作モード
SVC(スーパーバイザコール) リセット例外とSVCを処理するモード 特権モード
FIQ FIQ割り込みを処理するモード
IRQ IRQ割り込みを処理するモード
アボート メモリアクセス違反を処理するモード
未定義 未定義命令を処理するモード
システム ユーザモードと同じレジスタを使用する特権モード
ユーザ アプリケーションを実行するモード 非特権モード

プロセッサモードの変更方法

cpsrのMODE[4:0](0ビットから4ビット)を設定することで、プロセッサモードが変更可能です。

図1

【cpsrのビット割り当て】

cpsrの特定フィールドにライトするフィールド設定が用意されています。

図2
【プロセッサモード設定一覧】
動作モード プロセッサモード設定値 備考
SVC 10011(0x13)
FIQ 10001(0x11)
IRQ 10010(0x12)
アボート 10111(0x17)
未定義 11011(0x1b)
システム 11111(0x1f) ユーザモードと同一レジスタを使用する特権モード
ユーザ 10000(0x10)

スタックポインタの初期化方法

プロセッサモードが変化することで、スタックポインタr13が自動的に切り替わるので、スタックポインタのアドレスは8バイト境界に確保し、SVC(スーパーバイザ)モード、割り込み禁止状態での初期化が必要です。

スタックを初期化する場合、次の手順で行います。

  • ①MSR命令にて、cpsr_cフィールドを指定し、cpsr(カレントプログラムステータスレジスタ)のMODE[4:0]を書き換えます。
  • ②LDR疑似命令にて、スタックポインタを初期化します。
								    MSR    CPSR_c,<IRQ/FIQ割り込み禁止+プロセッサモード設定値> 
								    LDR    SP,=<スタックアドレス>
								

【サンプルプログラム】

								Mode_USR  EQU 0x10   ; CPSR.mode[4:0]ユーザモード設定値
								Mode_FIQ  EQU 0x11   ; CPSR.mode[4:0]FIQモード設定値
								Mode_IRQ  EQU 0x12   ; CPSR.mode[4:0]IRQモード設定値
								Mode_SVC  EQU 0x13   ; CPSR.mode[4:0]スーパーバイザモード設定値
								Mode_ABT  EQU 0x17   ; CPSR.mode[4:0]アボートモード設定値
								Mode_UND  EQU 0x1B   ; CPSR.mode[4:0]未定義モード設定値
								Mode_SYS  EQU 0x1F   ; CPSR.mode[4:0]システムモード設定値

								I_Bit      EQU	0x80   ; IRQビット定義
								F_Bit      EQU	0x40   ; FIQビット定義

								    MSR    CPSR_c,#Mode_IRQ:OR:I_Bit:OR:F_Bit ; IRQモードへ変更
								    LDR    SP,=<IRQ_STACK_TOP>                ; IRQモードモードスタックを初期化

								    MSR    CPSR_c,#Mode_FIQ:OR:I_Bit:OR:F_Bit ; FIQモードへ変更
								    LDR    SP,= <IRQ_STACK_TOP>               ; FIQモードスタックを初期化

								    MSR    CPSR_c,#Mode_UND:OR:I_Bit:OR:F_Bit ; 未定義モードへ変更
								    LDR    SP,= <UND_STACK_TOP>               ; 未定義モードスタックを初期化

								    MSR    CPSR_c,#Mode_ABT:OR:I_Bit:OR:F_Bit ; アボートモードへ変更
								    LDR    SP,= <ABT_STACK_TOP>               ; アボートモードスタックを初期化

								    MSR    CPSR_c,#Mode_SVC:OR:I_Bit:OR:F_Bit ; スーパーバイザモードへ変更
								    LDR    SP,= <SVC_STACK_TOP>               ; スーパーバイザモードスタックを初期化

								    MSR    CPSR_c,#Mode_SYS:OR:I_Bit:OR:F_Bit ; システムモードへ変更
								    LDR    SP,= <SYS_STACK_TOP>               ; システムモードスタックを初期化
								

スタックアドレスの定義方法

ターゲットのメモリ配置設定(ROM/RAMなどの設定)は、リンカの設定で行うことが一般的です。プログラムの中で、個別に設定することも可能ですが、メモリ配置を変更する場合、複数のファイルを修正することになり、修正漏れによる不具合が発生する場合があります。

ARMコンパイラの場合は、スキャッタローディングと呼ばれる方法で、スキャッタローディングファイルで設定したアドレス設定値をアセンブリ言語またはC/C++言語で確認できます。
アセンブリ言語の場合は、“||Image$$<Name>$$ZI$$Limit||”(<Name>はスキャッタローディングファイルで設定された領域名称です)で確認できます。
詳しくは、「ARMコンパイラ armlinkユーザガイド」を参照ください。

スキャッタローディングファイル例

スキャッタローディングファイルで、0x80000000番地を先頭に、各種スタックは16Kバイト単位で領域を確保します。

								USR_STACK  0x80000000  ALIGN 8 EMPTY 0x4000  ; ユーザ/システムモードスタックを確保。
								{
								}
								IRQ_STACK  +0  ALIGN 8 EMPTY 0x4000  ; IRQスタックを確保
								{
								}
								FIQ_STACK  +0 ALIGN 8 EMPTY  0x4000  ; FIQモードスタックを確保
								{
								}
								UND_STACK  +0 ALIGN 8 EMPTY 0x4000   ; 未定義モードスタックを確保
								{
								}
								ABT_STACK  +0 ALIGN 8 EMPTY 0x4000   ; アボートモードスタックを確保
								{
								}
								SVC_STACK  +0 ALIGN 8 EMPTY 0x4000   ; スーパーバイザモードスタックを確保
								{
								}
								

アセンブルソースファイル例

スキャッタローディングファイルのアドレス設定値を参照します。

								;
								; スキャッタローディングファイルからスタックアドレスを参照します。
								;
								  IMPORT ||Image$$USR_STACK$$ZI$$Limit||   ; ユーザモードスタックアドレス
								  IMPORT ||Image$$IRQ_STACK$$ZI$$Limit||   ; IRQモードスタックアドレス
								  IMPORT ||Image$$FIQ_STACK$$ZI$$Limit||   ; FIQモードスタックアドレス
								  IMPORT ||Image$$UND_STACK$$ZI$$Limit||   ; 未定義モードスタックアドレス
								  IMPORT ||Image$$ABT_STACK$$ZI$$Limit||   ; アボートモードスタックアドレス
								  IMPORT ||Image$$SVC_STACK$$ZI$$Limit||   ; スーパーバイザモードスタックアドレス
								…
								…
								  MSR    CPSR_c,#Mode_IRQ:OR:I_Bit:OR:F_Bit ;  IRQモードへ変更
								  LDR    SP,= ||Image$$IRQ_STACK$$ZI$$Limit||  IRQモードのモードスタックを初期化