=====================================================================
                         ARM-Mプロセッサ依存部設計メモ
                                  Last Modified: 23 Aug 2008 21:54:54
=====================================================================

○このドキュメントの位置づけ

このドキュメントは，TOPPERS/ASPカーネルをARMvX-Mプロセッサに移植するため
の設計メモである．


○ARMVx-Mの仕様まとめ

ARMvX-Mの仕様のうち，カーネルの設計に関係する事項についてまとめる．

●レジスタ

汎用レジスタはR0〜R15の16種類あり，R13のみが2バンク構成（PSP,MSP）とな
っている．R15はPC, R14はリンクレジスタ（LR）となっている．R0〜R3,R12は
スクラッチレジスタである．

●コーリングコンベンション

R0〜R4が引数，それ以上はスタック．戻り値は，R0〜R1に格納される．(ARMに
より規定されているため，コンパイラに依存せずこのルールとなる．)

●CONTROLレジスタ

PSP,MSPの切り替え，PrivilageとUserモードのレジスタ．変更後は，インスト
ラクションバッファフラッシュ命令を実行する必要がある（isb）．CONTROLレ
ジスタの詳細は，ARMv7-M Architecture Application Level Reference 
Manual の B1-9 を参照のこと．

●割込みベクタ

ベクタテーブル型で，ベクタテーブルのアドレスは，リセット時は0x00で，
Vector Table Offset Register（メモリマップドレジスタ） を操作すること
で，任意のアドレスに配置可能である．

●優先度

値が小さい方が高優先度となる．

優先度は最大8bitであり，SoC毎に実装されているビット幅が異なる．実装さ
れるビットが8bit以下の場合は，LSBから無効になる．例えば，実装されてい
るビット幅が7bitの場合は，ビット0が無効となる．

優先度のビットフィールドのLSBから数ビットをサブ優先度と呼ぶフィールド
に設定することが可能である．残りの上位ビットをプリエンプション優先度と
呼ぶ．プリエンプション優先度が同じで，サブ優先度が異なる優先度のグルー
プは，お互いをプリエンプトすることができない．

Reset,NMI,Hard Fault 以外の例外は割込みと同様に優先度が設定可能であり，
割込みマスク機能により，発生を禁止することが可能である．

●CPUモード

プロセッサは，ThreadモードもしくはHandlerモードのいずれかのモードとな
る．

●リセット時の状態

リセット時はThreadモード，MSPが有効となっている．

●Handlerモード

例外/割込みを受け付けると遷移するモード．受け付けた例外/割込みの例外番
号が，IPSRにセットされる．例外番号は，TRMで定められている番号である．

        例外              例外番号
  Reset                      1
  Non-makable Interrupt      2
  Hard Fault                 3
  Memory Management          4
  Bus Fault                  5
  Usage Fault                6
  SVCall                    11
  Debug Monitor             12
  PendSV                    14
  SysTick                   15
  IRQ0                      16
  IRQ1                      17
  ..

例外/割込みを受け付けると，受け付けた例外/割込みの優先度以下の例外/割
込みを禁止する．この優先度マスクを"NVIC優先度マスク"と呼ぶ．この優先度
は，ソフトウェアから変更することができず，例外/割込みのリターンにより
割込み前の値に自動的に戻る．

●スタックポインタ（PSPとMSP）

スタックポインタは，PSPとMSPがあり，排他的に使用可能である．Handlerモ
ードではMSPのみ使用可能であり，ThreadモードではCONTROLレジスタで選択可
能である．CONTROLレジスタの1ビット目をセットするとPSPが有効に，クリア
すると，MSPが有効になる．

●ThreadモードとHandlerモードの遷移

ThreadモードからHandlerモードへの遷移は，例外/割込みを受け付けることで
発生する．一方，HandlerモードからThreadモードへの遷移は，PCに
EXC_RETURN(0xfffffffx)の値を設定することにより行う（例外リターン処理と
呼ぶ）．EXC_RETURNの下位4bitにより，遷移先のモードや使用するスタックポ
インタを変更可能である．例外リターンにより，PRIMASKやBASEPRIの値は変化
しない．一方，FAULTMASKの値は'0'にクリアさせる．

●EXC_RETURN

例外/割込み受付け時にlrに設定される値．ビット31〜4ビットは全て'1'で，
下位4bitは，受付け時のCPUモードやスタックを反映した値となっている．

 0b0001 : Handlerモード
 0b1001 : Threadモード with MSP
 0b1101 : Threadモード with PSP

●ThreadモードとHandlerモードの判定

現状のモードを判定するには，IPSRを見て，'0'ならThreadモード，それ以外
なら，Handlerモードとなる．

●BASEPRIレジスタ

設定した優先度以下の優先度の割込みの受付を禁止する．この優先度マスクを
"BASEPRI優先度マスク"と呼ぶ．'0'を設定すると，全ての割込みを許可する．
例外/割込みの受付とリターンにより変化しない．例外/割込みに対する割込み
優先度マスクは，NVIC優先度マスクとBASEPRIの設定値の高い方（値が小さい
方）となる．

●FAULTMASK

FAULTMASKは'1'をセットすることにより，NMI以外の全ての割込みを禁止する．
FAULTMASKは，例外のリターン処理により'0'にクリアさせる．

●PRIMASKとWFI

PRIMASKを'1'に設定すると，NMI と Hardware Fault 以外の例外/割込みを禁
止する．PRIMASKは割込みの許可と割込み待ちをアトミックに行うために用い
る．具体的には，PRIMASKがセットされている状態でwfiを実行すると，割り込
み待ちとなり，割込み受付けるとハンドラを実行せずに，wfiからリターンし
てくる．

●例外/割込みの受付

・例外/割込みを受付けると，受付け時にアクティブなスタック上に以下のコ
  ンテキストを保存する(例外フレームと呼ぶ)．

   -----------
  |    R0     |  <- new SP
   -----------
  |    R1     |
   -----------
  |    R2     |
   -----------
  |    R3     |
   -----------
  |    R12    |
   -----------
  |    LR     |
   -----------
  |    PC     |
   -----------
  |   xPSR    |
   -----------
  |           | <- old SP

・プロセッサをHandlerモードとする．MSPが有効となる．
・受付けた例外/割込みの例外番号をIPSRに設定する．
・NVIC割込み優先度マスクを受付けた例外/割込みの優先度に設定する．
・lrにEXC_RETURNの値が設定される．
・ベクタテーブルを読み込みハンドラを実行する．

●例外/割込みからのリターン

pcにEXC_RETURNの値を設定することにより，例外/割込みからリターンする．
pcへの設定に使用可能な命令には制限があり，以下命令が使用可能である．

  ・POP/LDM, LDR, BX

●未解決課題

・ベクターテーブルに登録する関数のアドレスのLSBは'1'にするべきか?
・NVICは例外・割込みのネスト回数を内部的に管理しているらしい．
  (!リファレンスを明らかに)．
    ソフトウェアでは，ネストの帳尻を合わせれば，リターンスタックを偽造し
    ても問題ないか．


○OSの実装

1.ターゲット名

 1-1 cm3(Cortex-M3)
 1-2 armv7m(ARMV7M)
 1-3 arm_m

cm3では，Coretex-M1(armv6-m)をサポートする場合に問題となる．armv7mでは，
armv8mがリリースされた場合に問題となる．ARM依存部はJSPでは，armv4とな
っていたが，armv5やarmv7も動作するためASPでは単にarmとした．そのため，
arm_mが無難と考えられる．


2.ThreadモードとHandlerモードの使い分け

 2-1
  タスクコンテキストはThreadモード，非タスクコンテキストはHandlerモー
  ドで動作させる．

 2-2
  タスクコンテキストと非タスクコンテキスト共にHandlerモードで動作させ
  る．

プロセッサの設計方針を考慮すると2-1が有力．2-1での問題点としては，割込
みハンドラからタスクへのリターン時にモードの変更が以下の様に多発するこ
とが挙げられる．

1.割込みハンドラ               : Handlerモード
2.タスク例外ハンドラの呼び出し : Threadモード
3.タスクへのリターン処理       : Handlerモード
4.タスクの再開                 : Threadモード

3でHandlerモードに移行する必要があるのは，例外フレームを用いて復帰する
には，Handlerモードで例外リターン処理を行う必要があるためである．ARMで
は，複数レジスタのロードとCPSRの復帰を同時に行えるが，M3は行えないため，
この方法で割込み先のタスクにリターンする必要がある．

2-2の場合の割込みハンドラからタスクへのリターン時にモードの変更を以下
に示す．また，2-2では割込み優先度の最低値をタスクの実行時の優先度とし
てリザーブする必要がある．

1.割込みハンドラ               : Handlerモード
2.NVIC優先度マスク'0'を0へ     : Threadモード
3.最低優先度のHandlerモードへ  : Handlerモード
4.タスク例外ハンドラの呼び出し : Handlerモード
5.タスクへのリターンの前処理   : Threadモード
3.タスクへのリターン           : Handlerモード
4.タスクの再開                 : Handlerモード

割込みハンドラからタスクのリターンに関しては，2-2であっても，2を実行す
る場合に，NVIC優先度マスクを'0'にするため，例外リターン処理を行う必要
がある．また，NVIC自体が，割込みのネスト回数を管理しているため，3から4
への遷移のために，いったん例外/割込みを受付けた状態にする必要があるた
め，結果的に2-1以上の遷移が必要となる．

2-2の場合は，MSPしか使えないため，割込みの入り口でネスト回数を判断して，
スタックを入れ替える必要がある．

HRP等でメモリ保護を用いる場合は2-1となる．

以上の理由により，2-1を採用する．ただし2-1は，カーネル起動時とIDLEルー
プの扱いを検討する必要がある．これらについては別途議論する．


3.ディスパッチャの実行モード

 3-1
  Threadモードで実行する

 3-2
  Handlerモードで実行する

ディスパッチャをThreadモードで実行すると，割込みによりプリエンプトされ
たタスクに戻る場合は次のようなパスになる．

 1. ディスパッチャ呼び出し : Threadモード 
 2. ディスパッチャ実行     : Threadモード
 3. タスク例外実行         : Threadモード
 4．タスクへのリターン処理 : Handlerモード
 5. タスクの再開           : Threadモード

割込みハンドラから自らディスパッチしたタスクへリターンする場合は次のパ
スになる．

 1.割込みハンドラ               : Handlerモード
 2.ディスパッチャ実行           : Threadモード
 3.タスク例外ハンドラの呼び出し : Threadモード
 4.タスクへのリターン           : Handlerモード
 5.タスクの再開                 : Threadモード

一方，ディスパッチャをHandlerモードで実行すると，割込みによりプリエン
プトされたタスクに戻る場合は次のようなパスになる．

 1. ディスパッチャ呼び出し : Threadモード 
 2. ディスパッチャ実行     : Handlerモード
 3. タスク例外実行         : Threadモード
 4．タスクへのリターン     : Handlerモード
 5. タスクの再開           : Threadモード

割込みハンドラの出口から自らディスパッチしたタスクへリターンする場合は
次のパスになる．

 1.割込みハンドラ               : Handlerモード
 2.ディスパッチャ実行           : Handlerモード
 3.タスク例外ハンドラの呼び出し : Threadモード
 4.タスクへのリターン           : Handlerモード
 5.タスクの再開                 : Threadモード

タスク例外ハンドラがないOSの場合は，Handlerモードで実行した方がモード
の遷移の回数が減るが，タスク例外ハンドラがあると，Threadモードの方が遷
移回数が減るため，Threadモードとする．

メモリ保護を考慮すると，ディスパッチャはHandlerモードで動作させた方が
効率がよいと考えられる（SVCでハンドラを呼び出すとHandlerモードとなるた
め）．


4.スタックの使い分け

 4-1
  タスクコンテキストをPSP, 非タスクコンテキストをMSP
 4-2
  タスクコンテキスト，非タスクコンテキスト共にMSP

4-2の場合，割込みの入り口でネスト回数を判断して，スタックを入れ替える
必要がある．2でタスクコンテキストはThreadモード，非タスクコンテキスト
はHandlerモードで動作させるとしたため，4-1を採用すると，割込みの入り口
で自動的にスタックが切り替わる．ThreadモードでのPSPのアクセスも，
mrs/msr命令で行えるため，4-1を採用する．


5.コンテキストの判定

 5-1
  IPSRが'0'(Threadモード)ならタスクタスクコンテキスト，'1'(Handlerモー
  ド)なら非タスクコンテキストとする．

 5-2
  割込みのネスト回数を保持する変数を用意．1以上で非タスクコンテキスト．

 5-3
  アクティブなスタックにより判断（MSPなら非タスクコンテキスト，PSPなら
  タスクコンテキストとする）

5-1は，ソフトウェア側でコンテキスト管理のための処理を行う必要がないと
いうメリットがある．しかしながら，カーネルの起動時Threadモードであるた
め，Handlerモードへ移行する必要がある．ASPカーネルでは，IDLEループ実行
は非タスクコンテキストとして動作させる必要があるため，IDLEループは
Handlerモードで動作させる必要がある．IDLEループはディスパッチャから呼
び出される．3で定めたように，ディスパッチャをThreadモードで動作させる
ため，IDLEループを呼び出す際には，Handlerモードへ遷移する必要がある．
Handlerモードへの遷移は，SVC/PendSVCを用いると実現可能であるが，6の割
込みにプリエンプトされたタスクへのリターン時のHandlerモードへの移行で
もSVC/PendSVCの使用が必要となるため，SVCハンドラでは，どの目的で呼び出
されたか判定する必要が出てくるため，オーバヘッドが増大する．

5-2では，カーネル起動時やIDLEループ時に変数を'1'に設定すればよいことに
なる．この場合，カーネル起動時やIDLEループ時にThreadモードで実行しても
動作に問題がないよう，特に割込みの出入り口の設計を注意する必要がある．

カーネル起動時に関しては，全割込みを禁止しており，割込みが入らないので
特に問題はない．IDLEループ時は，ThreadモードでMSPとPSPの選択が可能であ
ることを利用して，非タスクコンテキストのスタックであるMSPに変更する．
例外/割込みの入り口では，多重割込みであるかをEXC_RETURNのモード判定の
ビットではなく，スタックの判定ビットで行えば問題ない．例外/割込みから
のリターンに関しては，多重割込みの判定は，入り口と同様にEXC_RETURNのス
タック判定ビットで行えばよい．例外リターン処理時にpcに代入する
EXC_RETURNの値を一律0xfffffffd (Threadモード with MSP)とするのではなく，
例外/割込み受付け時にLRに設定されるEXC_RETURNを用いることにより，IDLE
ループに割り込んだ場合でも問題なくリターンする．

カーネル起動時は，MSPがアクティブであり，割込みハンドラ実行時はHandler
モードであることからMSPがアクティブでり，IDLEループ時にMSPをアクティブ
に設定すると，非タスクコンテキストは全て，MSPをアクティブにして動作す
ることになる．また，割込み時は割込み前にアクティブなスタックの情報が，
EXC_RETURNに設定される．そのため，コンテキストの判定は，割込みネスト回
数を保持する変数がなくとも，アクティブなスタックを見ればよいことになる．
また，exc_sense_context()に関しては，例外フレーム中にEXC_RETURNを追加
し，その内容により判断すればよい．以上の理由により，5-3を採用する．


6.割込みにプリエンプトされたタスクへのリターン時のHandlerモードへの移
  行方法

 6-1
  SVCを用いる
 6-2
  PendSVCを用いる

PendSVCとSVCの違いは，PendSVCが要求がキューイングされ，SVCは要求がキュ
ーイングされないことである．割込みにプリエンプトされたタスクへのリター
ン時のHandlerモードへの移行は，キューイングされずに即座に処理される必
要があるため，どちらで実現しても問題ない．どちらをカーネルのリソースし
て使用するかの選択だけである．

どちらを使うとしても，優先度の設定が問題となる．ディスパッチャから割込
みにプリエンプトされたタスクへのリターンまでの処理は，少なくともCPUロ
ック状態で実行されなければならない．SVCやPendSVCはどちらも割込み優先度
を持つため，NVIC優先度マスクよりBASEPRI優先度マスクの方が高い場合，処
理されない．

CPUロック状態をBASEPRIの設定で実現した場合，その設定値をSVCやPendSVCに
設定した値より低くする必要がある．言い換えると，SVCやPendSVCの優先度を
CPUロック時の優先度マスクの値より高い値（他の割込みより高い優先度）と
する必要がある．

CPUロック状態をFAULTMASKやPRIMASKで実現した場合は，これらが設定される
と，SVCやPendSVCが受付けられないため，いったんBASEPRIにより割込みをマ
スクするように設定する必要がある．この場合も，SVCやPendSVCは他の割込み
より高い優先度を設定する必要がある．

以上により，Handlerモードへの移行のためには，CPUロック状態をBASEPRIで
実現し，SVCやPendSVCに設定する優先度をカーネル管理内の最高優先度より一
つ高い優先度に設定する必要がある．


7. 例外/割込み出入り口での多重割込みの判断

7-1
 EXC_RETURNのモード判定ビット
7-2
 EXC_RETURNのスタック判定ビット
7-3
 割込みネスト回数の管理変数

例外/割込み受付け時は，受付けた例外/割込み以下の割込みは禁止するが，全
割込み禁止状態にはならない．そのため，割込みネスト回数の管理変数をイン
クリメントする前に割込みが入る可能性があるため，7-3は使用することがで
きない．

5で議論した通り，IDLEループをThreadモードで実行するため，7-1ではなく，
7-2で判断する必要がある．


8. IDLEループ

8-1
 Threadモードで実行
8-2
 Handlerモードで実行

5で議論した通り，Threadモードで実行できた方がオーバヘッドが小さい．ま
た，Threadモードで実行しても，割り込みの出入り口で正しく非タスクコンテ
キストと判定できれば，Threadモードで問題ない．


9．カーネル管理外の割込みのサポート

9-1
 カーネル管理外の割込みをサポートしない
9-2
 カーネル管理外の割込みをサポートする

ベクタテーブルをサポートしており，割込みハンドラもC言語で記述可能であ
るため，サポートが容易であるため，サポートする．


10. CPUロック

10-1
 BASEPRIを使用 
10-2 
 FAULTMASK/PRIMASKを使用

カーネルの管理外の割込みをサポートするなら，BASEPRIを使用する必要があ
る．


11. 割込みロックとCPU例外の関係

11-1
 BASEPRIを使用 
11-2 
 FAULTMASK/PRIMASKを使用 

FAULTMASK/PRIMASKを使用すると，NMI と Hardware Fault 以外のCPU例外も禁
止されてしまう．

BASEPRIを用いると，割込みロック中にもCPU例外を受付けたい場合は，
BASEPRIを用いて，最高優先度をCPU例外のためにリザーブする必要がある．

割込みロック時も，CPU例外を受付けるようにしたければBASEPRIを使用する必
要がある．

μIRON4.0仕様の3.5.3では，CPU例外の優先度は次のように定められている．

"CPU例外ハンドラの優先順位は，CPU例外が発生した処理の優先度と，ディス
パッチャの優先順位のいずれかよりも高い．"

CPU例外が発生した処理の優先度よりも高いとあるので，CPUロックや割込みロ
ック状態のタスクで発生した場合でも，優先して実行されるべきだとも考えら
れる．

一方，TOPPERS標準割込み処理モデルの仕様書では，CPU例外は，プロセッサ毎
に異なるため，CPU例外の処理モデルの標準化検討の対象外としている．その
ため，ARM-Mでの扱いを決めて，マニュアルに明記すればよいと考えられる．


12. 外部優先度と内部優先度の変換

外部優先度とはAPIで指定する割込み優先度(PRI型)のことであり，値が小さい
ほど優先度が高い．割込みハンドラには，-1から連続した負の値を設定可能で
ある．内部優先度は，BASEPRIやNVICの優先度レジスタに設定する値である．

実装される割込み優先度のビット幅を TBITW_IPRI とすると，設定可能な外部
優先度は次のようになる．

  TIPM_ENAALL（＝0）〜 -(1 << TBITW_IPRI)


13. カーネル管理内の最高優先度(CPUロック状態での優先度マスク)

6.で述べたように，割込みの出口でSVCハンドラを呼び出す必要があるため，
SVCハンドラはCPUロック状態のBASEPRIに設定する優先度マスクより高い優先度
を設定する必要がある．

実装される割込み優先度のビット幅を TBITW_IPRI，優先度中のサブ優先度の
ビット幅をTBIT_IPRIとすると，CPUロック状態（カーネル管理内割込みに設定
可能な最高優先度）として指定可能な優先度マスクの設定範囲は以下の値の範
囲となる．

    -(2^(TBIW_IPRI)) + (2^TBITW_SUBIPRI)) 〜 -1


14. 未解決課題

・割込みロックとCPU例外の関係
  BASEPRIを使ったとしても，あるCPU例外処理中に他の例外が発生すると，そ
  の例外は受け付けられないため，ITRON仕様は満たせない．
  ->あきらめてマニュアル記載に逃げるか.
  メモリプロテクションの例外もマスク可能であるため要件等．


以上．
