【ベアメタル開発】割り込みベクタテーブルを理解する(割り込みベクタオフセットを設定する理由)

以前の LED ドライバーには割り込みがなく、割り込みの初期化も含まれていません。プログラムに割り込みが含まれている場合、他に何を初期化する必要がありますか? この問題を解決するには、まず割り込みシステムに何が含まれているかを理解する必要があります。

①割り込みベクタテーブル割り込みに対応する割り込みサービス関数を記述し、プログラムの実行開始時に保存します。デフォルトは0x00000000です。

割り込みコントローラ(NVIC、GIC) :割り込みシステムの管理組織

③割り込みイネーブル: 特定のペリフェラルの割り込みイネーブル (特定のペリフェラルの割り込みを使用するには、最初にこのペリフェラルの割り込みを有効にする必要があります)

割り込みサービス関数: 割り込みが発生すると、割り込みサービス関数が呼び出されます (割り込み処理ロジックは割り込みサービス関数内にあります)。


目次

1. 割り込みベクタテーブル

1. 割り込みベクタテーブルとは何ですか?

2. 割り込みの種類

2. 割り込みベクタテーブルのオフセットを設定する必要があるのはなぜですか? 

1. 原因分析

2. オフセットの求め方

3. 設定方法

3. 割り込みベクタテーブルフレームワークをコンパイルします。


1. 割り込みベクタテーブル

1. 割り込みベクタテーブルとは何ですか?

割り込みベクタテーブルの機能は、割り込みに対応する割り込みサービス関数を記述することです。プログラムの実行を開始した場所に保存されます。デフォルトは 0x00000000 です。割り込みベクタ オフセットを設定することで、割り込みベクタ テーブルの場所を変更できます。

Cortex-M の割り込みベクタ テーブルにはすべての割り込みがリストされており、各割り込みは割り込みサービス関数に対応しています。一方、Cortex-A の割り込みベクトル テーブルでは割り込みが 7 つのカテゴリに分割されています。

  • 割り込みが発生したら、まずそれがどのカテゴリに属する​​かを判断します
  • 次に、割り込みベクタ テーブルに移動して、対応するクラスの割り込みサービス関数を見つけます。
  • 次に、対応する割り込みサービス関数を実行します。
  • 最後に、プログラムが一時停止した次の場所に戻ります。

割り込みベクタテーブルには、各種割り込みに対応した割り込みサービス関数のアドレスが記述されています。

2. 割り込みの種類

割り込みは大きく7種類に分けられ、CPUの動作モードに応じて割り込みの種類が異なり、割り込みが発生するとCPUは対応するモードに切り替わってから割り込み処理を行います。その中で最も一般的に使用されるのは、リセットIRQ割り込みです。

  • リセット: CPU がリセットされた後、リセット割り込みに入ります。リセット割り込みサービス関数では、SP ポインターや DDR などの初期化作業を行うことができます。
  • 未定義命令:この割り込みは、命令を認識できない場合に生成されます。
  • SWI: Linux システム コールは SWI 命令を使用してソフト割り込みを発生させ、ソフト割り込みを通じてカーネル空間に入ります。
  • プリフェッチ中止:この割り込みは、プリフェッチ命令でエラーが発生したときに生成されます。
  • データ中止:この割り込みは、データへのアクセス中にエラーが発生したときに発生します。
  • IRQ :ペリフェラル割り込みによりこの割り込みが発生します
  • FIQ:高速割り込み。割り込みを迅速に処理する必要がある場合は、この割り込みを使用できます。
オフセットアドレス 割り込みの種類 割り込みモード
0x00 リセット割り込み(リセット) SVC
0x04 未定義の命令 未定義
0x08 ソフトウェア割り込み (SWI) SVC
0x0C 命令プリフェッチアボート割り込み(Prefetch Abort) アボート
0x10 データアクセスアボート割り込み(Data Abort) アボート
0x14 予約済み -
0x18 IRQ 中断(IRQ Interrupt) IRQ
0x1C FIQ 中断(FIQ Interrupt) FIQ

注:割り込みベクタ テーブルはプログラム実行の開始位置に配置されるため、ここでのオフセット位置は開始位置からの相対位置になります。

2. 割り込みベクタテーブルのオフセットを設定する必要があるのはなぜですか? 

1. 原因分析

割り込みベクタ テーブルはプログラム実行の開始位置に保存され、デフォルトは 0x00000000 です。リファレンスマニュアルのメモリマッピングテーブルによると、0x00000000の場所には、デバイスの電源投入時の起動に関わる内容であるブートROMが保存されていることが分かりましたが、内容の他の部分を占有しないようにするため、割り込みベクタテーブルにオフセットを適用します。

割り込みコントローラ(GIC)は、割り込みベクタテーブルのオフセット位置を設定できます。ベア メタル開発環境では、通常、割り込みサービスのリセット関数で割り込みベクタ テーブルの場所を手動で指定します。OS環境では、OS が割り込みコントローラを初期化し、割り込みベクタ テーブルを別のアドレス空間に配置します。予約中です。

2. オフセットの求め方

RAM と DDR の範囲を考慮して、通常はプログラムを DDR に保存します。

  • RAM : CPU 内の利用可能なメモリのセクション imx6ull 内部 RAM のサイズは 128K (0X900000~0X91FFFF)
  • DDR : SOC にパッケージ化された CPU の外部のメモリ。DDR のサイズは 256M または 512M (2048 個あるように見えますが、バスの制約により CPU がアクセスできるサイズは 256M です)

DDRの方が範囲が広いので、その後のシステム移植を考慮してDDRを使用します。割り込みベクタ テーブルが 0x83000000 に設定されていると仮定すると、実際には、割り込みが発生したときに 0x83000000 の位置に移動して、対応する割り込みサービス関数を見つけるように CPU に指示します。

実際、割り込みベクタ テーブルとストレージ アドレスを 0x87800000 に設定することもできます。

3. 設定方法

アセンブリ経由で設定

アセンブリ形式の場合は、デバイスの電源がオンになるとリセット割り込みがトリガーされ、その後リセット割り込みサービス関数が実行されるため、それをリセット割り込みサービス関数に配置することをお勧めします。

/* 复位中断服务函数 */ 
Reset_Handler:
    /* ... */

    ldr r0, =0x87800000         /* 设置中断向量表的偏移 */
	dsb
	isb
	mcr p15, 0, r0, c12, c0, 0
	dsb
	isb

    /* ... */

C 関数を呼び出す

割り込みが発生する前に設定されていれば、C 関数を直接呼び出して割り込みベクタ テーブルを設定できます。通常、初期化を中断する関数に配置されます。

/* 设置中断向量表偏移 */
__set_VBAR((uint32_t)0x87800000);

3. 割り込みベクタテーブルフレームワークをコンパイルします。

1. 部分的なプロセス

割り込みベクタ テーブルはプログラム実行の開始位置に保存され、デフォルトは 0x00000000 です。実際、各割り込みに対応する割り込みサービス関数がどこにあるかをカーネルに伝えています。リセット割り込みのサービス関数アドレスを設定するとします。

.global _start
 
_start:
    /* 把 Reset_Handler 的地址保存到 pc 指向的位置 */ 
    ldr pc, =Reset_Handler          


/* 复位中断服务函数 */ 
Reset_Handler:
    b Reset_Handler      @ 暂时先死循环,后面再修改              

pc はプログラム カウンタで、次に実行する命令のアドレスを保存するために使用されます。デフォルトでは、プログラムは 0x00000000 から実行を開始します、つまり、PC が最初に取得するアドレスは 0x00000000 ですが、ここで、Reset_Handler のアドレスは実際には 0x00000000 の場所に保存されます。

次に、pc は次のアドレス、0x00000004 を指すようになります。

2. 全体の枠組み

他の割り込みも同様で、pc レジスタのアドレスの自動インクリメント機能を利用して、各割り込みサービス関数のアドレスを 1 つずつ設定します。具体的な実装としては、リセット割り込みとIRQ割り込みの組み立て部分を示します。

リセット割り込み:一般にリセット時または電源投入直後にのみトリガーされ、特別に実装する必要のあるロジックがないため、アセンブリ部分のみが含まれます。リセット割り込みサービス関数(アセンブリ部分)

②  IRQ 割り込み:アセンブリ部分と C コード部分が含まれます。

  • アセンブリ パーツは環境を初期化するために使用されます
  • C コード部分はロジックの実装に使用されます。
_start:
    ldr pc, =Reset_Handler          /* 复位中断 */ 
    ldr pc, =Undefined_Handler      /* 未定义指令中断 */
    ldr pc, =SVC_Handler            /* SVC(Supervisor)中断*/
    ldr pc, =PrefAbort_Handler      /* 预取终止中断 */
    ldr pc, =DataAbort_Handler      /* 数据终止中断 */
    ldr pc, =NotUsed_Handler        /* 保留中断 */
    ldr pc, =IRQ_Handler            /* IRQ 中断 */
    ldr pc, =FIQ_Handler            /* FIQ(快速中断) */

/* 复位中断 */ 
Reset_Handler:
    b Reset_Handler

/* 未定义指令中断 */ 
Undefined_Handler:
    b Undefined_Handler

/* SVC */ 
SVC_Handler:
    b SVC_Handler

/* 预取终止中断 */ 
PrefAbort_Handler:
    b PrefAbort_Handler

/* 数据终止中断 */ 
DataAbort_Handler:
    b DataAbort_Handler    

/* 保留中断 */ 
NotUsed_Handler:
    b NotUsed_Handler 

/* IRQ 中断 */ 
IRQ_Handler:
    b IRQ_Handler   

/* FIQ(快速中断) */ 
FIQ_Handler:
    b FIQ_Handler  

おすすめ

転載: blog.csdn.net/challenglistic/article/details/131209282