ARM7/9/11、Cortex-A、Cortex-R の割り込み関数
IAR C/C++コンパイラfor Armは、Arm7/9/11、Cortex-A、およびCortex-R デバイスの割り込み関数に関連する以下の基本関数を提供します。
拡張キーワード:
__irq、__fiq、、__nested、組込み関数:
__enable_interrupt、__disable_interrupt、__get_interrupt_state、__set_interrupt_state
注記
Cortex-Mは、他のArmデバイスとは割り込みメカニズムが異なります。また、これらのデバイスとは使用できるプリミティブセットが異なります。詳細については、Cortex-Mデバイスの割り込み関数を参照してください。
割り込み関数
組み込みシステムでは、ボタン押下の検出など、外部イベントを即座に処理するために割り込みを使用します。
割り込みサービスルーチン
通常、コード中で割り込みが発生すると、コアはすぐにコードの実行を停止し、その代わりに割り込みルーチンの実行を開始します。 割り込み処理の完了後、割り込まれた関数の環境を復元することが重要です。これには、プロセッサレジスタの値やプロセッサステータスレジスタの値の復元も含まれます。これにより、割り込み処理用コードの実行が終了したときに、元のコードの実行を続行できます。
コンパイラは、割り込み、ソフトウェア割り込み、高速割り込みをサポートします。割り込みタイプごとに、割り込みルーチンを記述できます。
すべての割込み関数は、Armモードでコンパイルする必要があります。Thumbモードを使用する場合、__arm拡張キーワードまたは#pragma type_attribute=__armディレクティブを使用して、デフォルトの動作をオーバライドします。これはCortex-Mデバイスでは適用されません。
割り込みベクタと割り込みベクタテーブル
各割り込みルーチンは、Armコアのドキュメントで指定されている、例外ベクタテーブルのベクタアドレス/命令に関連付けられます。割り込みベクタは、例外ベクタテーブルへのアドレスです。ARMコアの場合は、例外ベクタテーブルはアドレス0x0から開始します。
デフォルトでは、ベクタテーブルは無限ループするデフォルト割り込みハンドラで構成されます。明示的な割り込みサービスルーチンを持たない各割り込みソースに対しては、デフォルトの割り込みハンドラが呼び出されます。特定のベクタに自分のサービスルーチンを記述する場合、そのルーチンはデフォルトの割り込みハンドラを上書きします。
割り込み関数の定義 — 例
割り込み関数を定義するには、__irqまたは__fiqキーワードを使用できます。以下に例を示します。
__irq __arm void IRQ_Handler(void)
{
/* Do something */
}割り込みベクタテーブルの詳細については、Armコアのドキュメントを参照してください。
注記
割り込み関数のリターン型はvoidでなければならず、パラメータの指定は一切できません。
割り込みとC++メンバ関数
静的メンバ関数だけが割り込み関数になれます。非静的メンバ関数を呼び出す際には、オブジェクトに割当てる必要があります。割り込みが発生し、割り込み関数が呼び出されるときは、メンバ関数の割り当てに使用可能なオブジェクトは存在しません。
例外関数のインストール
すべての割り込み関数およびソフトウェア割り込みハンドラは、ベクタテーブルにインストールする必要があります。これは、システム起動ファイルcstartup.sのアセンブラ言語で行われます。
標準ランタイムライブラリでのArm例外ベクタテーブルのデフォルトの実装は、無限ループを実装する事前定義関数にジャンプします。そのため、アプリケーションで扱われないイベントに対して発生する例外は、無限ループ(B. )になります。
事前定義関数は、weakシンボルとして定義されます。weakシンボルは、重複するシンボルがない限り、リンカにより使用されます。別のシンボルが同じ名前で定義されている場合、これが優先されます。そのためアプリケーションは、正しい名前を使用するだけで、独自の例外関数を定義できます。
以下の例外関数名は、cstartup.sで定義され、ライブラリ例外ベクタコードで参照されます。
Undefined_Handler SVC_Handler Prefetch_Handler Abort_Handler IRQ_Handler FIQ_Handler
独自の例外ハンドラを実装するには、上記のリストから適切な実行関数名を使用して関数を定義します。
たとえば、Cに割込み関数を追加するには、IRQ_Handlerという名前の割込み関数を定義します。
__irq __arm void IRQ_Handler()
{
}割り込み関数は、Cリンケージを持つ必要があります。詳細については、呼び出し規約を参照してください。
C++を使用する場合の割り込み関数の例を以下に示します。
extern "C"
{
__irq __arm void IRQ_Handler(void);
}
__irq __arm void IRQ_Handler(void)
{
}その他の変更は必要ありません。
ネスト割込み
割り込みは、割り込みハンドラが開始される前に、Armコアにより自動的に禁止されます。割り込みハンドラが割り込みを再び有効にし、関数を呼び出して別の割り込みが発生した場合、LRに格納されている割り込み関数のリターンアドレスは、2番目のIRQが取得されるときにオーバライドされます。また、SPSRの内容は、2番目の割り込みが発生したときに破棄されます。__irqキーワード自体は、LRおよびSPSRを保存し復元しません。ネストされた割り込みを処理するときに必要な必須ステップを割り込みハンドラで実行するには、__irqのほかに、キーワード__nestedを使用する必要があります。ネストされた割り込みハンドラに対してコンパイラが生成する関数プロローグ(関数入口シーケンス)は、IRQモードからシステムモードに切り替わります。IRQスタックおよびシステムスタックの両方が設定されていることを確認してください。デフォルトのcstartup.sファイルを使用する場合、両方のスタックは正しく設定されます。
ネストされた割り込みを可能にするコンパイラにより生成された割り込みハンドラは、IRQ割り込みのみでサポートされます。FIQ割り込みは、迅速な提供を目的としているので、通常、ネストされた割り込みのオーバヘッドは非常に大きくなります。
以下の例は、Armベクタ割り込みコントローラ(VIC)でネストされた割り込みを使用する方法を示します。
__irq __nested __arm void interrupt_handler(void)
{
void (*interrupt_task)();
unsigned int vector;
/* Get interrupt vector. */
vector = VICVectAddr;
interrupt_task = (void(*)()) vector;
/* Allow other IRQ interrupts to be serviced. */
__enable_interrupt();
/* Execute the task associated with this interrupt. */
(*interrupt_task)();
}注記
__nestedキーワードでは、プロセッサモードがユーザモードまたはシステムモードのいずれかであることが必要です。
ソフトウェア割り込み
ソフトウェア割り込み関数は、ソフトウェア割り込みハンドラ(ディスパッチャ)を必要とし、実行中のアプリケーションソフトウェアから起動され(呼び出され)ます。また、引数を使用し、値を返します。このため、他の割り込み関数より少し複雑になります。ここでは、ソフトウェア割り込み関数を呼び出すメカニズムと、ソフトウェア割り込みハンドラが実際のソフトウェア割り込み関数をディスパッチする方法について説明します。
ソフトウェア割り込み関数の呼び出し
ソフトウェア割込み関数をアプリケーションソースコードから呼び出すには、アセンブラ命令SVC #immedを使用します。ここで、immedはソフトウェア割込み番号と呼ばれる整数値です(このガイドでは、svc_numberとも表記)。コンパイラでは、この命令をC/C++ソースコードから暗黙的に生成するための簡単な方法を提供しています。関数を宣言する際に__svcキーワードおよび#pragma svc_numberディレクティブを使用することによって生成できます。
__svc関数は、たとえば、以下のように宣言できます。
#pragma svc_number=0x23 __svc int svc_function(int a, int b);
この場合、アセンブラ命令SVC 0x23は、関数が呼び出されるときに生成されます。
ソフトウェア割り込み関数は、スタックの使用以外、パラメータおよびリターン値に関して通常の関数と同じ呼び出し規則に従います(呼び出し規約を参照)。
詳細については、__svcとsvc_numberを参照してください。
ソフトウェア割り込みハンドラと関数
割込みハンドラ(たとえばSVC_Handler)は、ソフトウェア割込み関数のディスパッチャとして機能します。割り込みハンドラは、割り込みベクタから呼び出され、ソフトウェア割り込み番号の取得と適切なソフトウェア割り込み関数の呼び出しを行う役割を持ちます。ソフトウェア割込み番号をC/C++ソースコードから呼び出す方法はないため、SVC_Handlerはアセンブラ内に記述する必要があります。
ソフトウェア割り込み関数
ソフトウェア割り込み関数は、C/C++で記述できます。__svcキーワードを関数定義で使用することにより、特定のソフトウェア割り込み関数に対する正しいリターンシーケンスがコンパイラで生成されます。割込み関数定義に#pragma svc_numberディレクティブは必要ありません。
詳細については、__svcを参照してください。
ソフトウェア割り込みスタックポインタの設定
ソフトウェア割り込みをアプリケーションで使用する場合には、ソフトウェア割り込みスタックポインタ(SVC_STACK)を設定し、スタックにエリアを割り当てる必要があります。SVC_STACKポインタは、他のスタックと一緒にcstartup.sファイルで設定できます。例としては、割り込みスタックポインタの設定を参照してください。SVC_STACKポインタの関連エリアは、リンカ設定ファイルで設定します(スタックメモリの設定を参照)。
割り込み処理
割り込み関数は、外部イベントが発生するときに呼び出されます。通常、別の関数の実行中に直ちに呼び出されます。割り込み関数の実行が終了すると、元の関数に戻ります。割り込まれた関数の環境の復元が必要で、これには、プロセッサレジスタやプロセッサステータスレジスタの値の復元が含まれます。
割り込みが発生すると、以下の処理が実行されます。
動作モードが、特定の例外に合わせて変化します。
例外の発生した次の命令のアドレスが、新しいモードの
R14に保存されるCPSRの古い値が、新しいモードのSPSRに保存されます。新たな割り込み要求は、
CPSRのビット7が設定され無効になり、例外が高速割り込みの場合、さらに高速割り込みがCPSRのビット6が設定され無効になります。PC が、対応するベクタアドレスでの実行開始を強制される
たとえば、ベクタ0x18の割り込みが発生した場合、プロセッサは、アドレス0x18でコードの実行を開始します。割り込みの開始位置として使用されるメモリエリアは、割り込みベクタテーブルと呼ばれます。割り込みベクタの内容は、通常、割り込みルーチンにジャンプする分岐命令です。
注記
割り込み関数により割り込みが有効にされると、割り込みルーチンから返される必要がある特殊なプロセッサレジスタは、破棄されたとみなされます。このため、返される前に復元できるように、これらを割り込みルーチンで格納する必要があります。__nestedキーワードが使用されている場合、この処理は自動的に行われます。