初期化処理とは
Cortex-A9シングルプロセッサのリセット例外から、main()関数までの動作を解説します。「セキュアワールド特権(第15回参照)で動作し、メモリ空間は0x80000000番地から1MバイトのRAM空間をノーマルメモリ・キャッシュ可能に設定し、他の領域は「ストロングリオーダメモリ(第10回参照)」に設定します。仮想アドレスと物理アドレスは、同一アドレスとして「MMU(第12回参照)」の設定をします。
リセット例外からmain()関数を呼び出すまでの初期化は、ユーザが作成する部分(ユーザーコード)とArmコンパイラが実行する部分(処理系ライブラリ)に分けることができます。コードのコピーや初期化変数/未初期化変数の初期化は、リンカのメモリ配置設定(スキャッタローディング記述ファイル設定)を処理系ライブラリが実行します。
例外ベクタテーブル
例外ベクタテーブルには、Arm命令(32ビット)の分岐命令を記述し、FIQ割り込みは例外ベクタテーブルの最終アドレスに配置されるので、直接割り込みハンドラを記述できます。詳しくは「第6回:ベクタテーブルと例外」を参照ください。
AREA VECTORS,CODE,READONLY ; VECTORS領域としてセクションを定義 ENTRY ;==================================================================== ; 例外ベクタテーブル ;==================================================================== EXPORT Start ; イメージエントリポイント設定用 Start ; イメージエントリアドレス設定 LDR PC,reset_addr ; リセット例外 LDR PC,undefined_addr ; 未定義命令例外 LDR PC,svc_addr ; スーパバイザコール例外 LDR PC,prefetch_addr ; プリフェッチアボート例外 LDR PC,abort_addr ; データアボート例外 B . ; 予約ベクタテーブル LDR PC,irq_addr ; IRQ割り込み LDR PC,fiq_addr ; FIQ割り込み ;==================================================================== ; 実行開始処理アドレス定義 ;==================================================================== reset_addr DCD reset_handler ; リセット例外ハンドラアドレス定義 undefined_addr DCD undefined_handler ; 未定義命令例外ハンドラアドレス定義 svc_addr DCD svc_handler ; スーパバイザコール例外ハンドラアドレス定義 prefetch_addr DCD prefetch_handler ; プリフェッチアボート例外ハンドラアドレス定義 abort_addr DCD abort_handler ; データアボート例外ハンドラアドレス定義 irq_addr DCD irq_handler ; IRQ割り込みハンドラアドレス定義 fiq_addr DCD fiq_handler ; FIQ割り込みハンドラアドレス定義 undefined_handler B undefined_handler ; 未定義命令例外発生の場合は永久ループ svc_handler B svc_handler ; スーパバイザコール例外発生の場合は永久ループ prefetch_handler B prefetch_handler ; プリフェッチア例外発生の場合は永久ループ abort_handler B abort_handler ; データアボート例外発生の場合は永久ループ irq_handler B irq_handler ; IRQ割り込み発生の場合は永久ループ fiq_handler B irq_handler ; FIQ割り込み発生の場合は永久ループ
スタックポインタの初期化
スタックポインタは、動作モードごとの設定が必要になりますので、動作モードを切り換えた後にスタックポインタを初期化します。スタックポインタが使用するメモリ空間は、スキャッタローディング記述ファイルに設定します。ユーザースタックポインタは、処理系ライブラリが設定しますので初期化の必要ありません。詳しくは「第5回:動作モード」を参照ください。
;==================================================================== ; CPSRモード設定フラグ定義 ;==================================================================== 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] システムモード設定値 ;==================================================================== ; スキャッタローディング記述ファイルからスタックアドレスを参照 ;==================================================================== 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|| ; スーパバイザモードスタック最終アドレス reset_handler ;==================================================================== ; 各モードスタックを初期化 ;==================================================================== CPS #MODE_IRQ ; IRQモードへ変更 LDR SP, =||Image$$IRQ_STACK$$ZI$$Limit|| ; IRQモードスタックを初期化 CPS #MODE_FIQ ; FIQモードへ変更 LDR SP, =||Image$$FIQ_STACK$$ZI$$Limit|| ; FIQモードスタックを初期化 CPS #MODE_UND ; 未定義モードへ変更 LDR SP, =||Image$$UND_STACK$$ZI$$Limit|| ; 未定義モードスタックを初期化 CPS #MODE_ABT ; アボートモードへ変更 LDR SP, =||Image$$ABT_STACK$$ZI$$Limit|| ; アボートモードスタックを初期化 CPS #MODE_SVC ; スーパバイザモードモードへ変更 LDR SP, =||Image$$SVC_STACK$$ZI$$Limit|| ; スーパバイザモードスタックを初期化
スキャッタローディング記述ファイル例
- スキャッタローディング記述ファイルで、プログラムは
0x80000000
番地を先頭に配置し、スタックポインタは0x80088000
番地から動作モード毎にスタック領域は16Kバイト単位で領域を確保します。 - 第1レベルテーブルは、
0x800F0000
番地から配置します。 - 初期化処理を行うソースファイル名は、
startup.s
で設定します。 - ユーザモードスタックは、
Arm_LIB_STACK
設定で、処理系ライブラリが設定します。 - ヒープ領域は、
Arm_LIB_HEAP
設定で、処理系ライブラリが設定します。
LOAD 0x80000000 0x20000000 { VECTORS 0x80000000 { startup.o(VECTORS,+FIRST) ; ベクタコード領域を配置 *(+RO) *(+RW) *(+ZI) } ; ; 0x80080000 ; Arm_LIB_HEAP 0x80080000 ALIGN 8 EMPTY 0x8000 { } Arm_LIB_STACK +0 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 { ; スーパバイザモードモードスタック } TTB 0x800F0000 EMPTY 0x4000 { ; 第1レベルテーブル } }
アドレス空間 | 配置内容 | サイズ |
---|---|---|
0x800F0000~0x800F3FFF | 第1レベルテーブルを配置 | 16Kバイト |
0x800A0000~0x800EFFFF | 未使用領域 | – |
0x8009C000~0x8009FFFF | 特権モードスタック領域(SVC_STACK) | 16Kバイト |
0x80094000~0x80097FFF | 未定義モードスタック領域(UND_STACK) | 16Kバイト |
0x80090000~0x80093FFF | FIQモードスタック領域(FIQ_STACK) | 16Kバイト |
0x80080000~0x8008FFFF | IRQモードスタック領域(IRQ_STACK) | 16Kバイト |
0x80088000~0x8008BFFF | ユーザモードスタック領域(Arm_LIB_STACK) | 16Kバイト |
0x80080000~0x80087FFF | アプリケーションヒープ領域(Arm_LIB_HEAP) | 32Kバイト |
0x80000000~0x8007FFFF | プログラム領域(VECTORS) | 512Kバイト |
TLB (トランスレーション・ルックアサイド・バッファ)の無効化
TLBを使用した仮想アドレスから物理アドレスへ変換し、TLBをキャッシュしますので無効化します。詳しくは「第13回:コプロセッサ設定」を参照ください。
MOV r0, #0 ; MCR p15, 0, r0, c8, c7, 0 ; TLBの無効化
分岐予測器のアレイの無効化
BTAC (Branch Target Address Cache)を無効化します。詳しくは「第13回:コプロセッサ設定例」を参照ください。
MOV r0, #0 ; MCR p15, 0, r0, c7, c5, 6 ; 分岐予測器アレイの無効化
命令/データキャッシュの無効化
リセット時に命令/データキャッシュの無効化します。詳しくは「第13回:コプロセッサ設定例」を参照ください。
; ; L1データキャッシュ設定値(32Kbyte/4Way) ; CACHE_LINE EQU 0xFF ; データキャッシュライン数 CACHE_WAY EQU 4 ; データキャッシュウェイ数 ;==================================================================== ; 命令キャッシュの無効化 ;==================================================================== MOV r0, #0 ; MCR p15, 0, r0, c7, c5, 0 ; 命令キャッシュの無効化 ;==================================================================== ; データキャッシュの無効化 ;==================================================================== MOV r1,#CACHE_LINE ; ライン数を設定(256ライン) Loop2 MOV r0,#CACHE_WAY ; ウェイ数を設(4ウェイ) Loop3 MOV r2, r0, LSL #30 ; ウェイ番号を設定 ORR r3, r2, r1, LSL #5 ; ライン番号を設定 MCR p15, 0, r3, c7, c6, 2 ; セット/ウェイ設定でキャッシュラインを無効化 SUBS r0, r0, #1 ; ウェイ番号を-1 BGE Loop3 ; ウェイ毎の初期化を実施 SUBS r1, r1, #1 ; セット番号を-1 BGE Loop2 ; セット毎の初期化を実施
MMUの設定
「第1レベルテーブルを使用したアドレス変換」機能を使用し、仮想アドレスと物理アドレスを同一アドレスとして初期化します。詳しくは「第12回:MMU」を参照ください。
PAGE_TABLE_STRONGLY EQU 2_00000000000000000000110111100010 PAGE_TABLE_NORMAL EQU 2_00000000000000000001110111101110 TTBR0_SETTING EQU 0x48 IMPORT ||Image$$TTB$$ZI$$Base|| ; TLBテーブル先頭アドレスを参照 ;==================================================================== ; Cortex-A9 MMUコンフィギュレーション設定 ;==================================================================== MOV r0, #0x0 ; MCR p15, 0, r0, c2, c0, 2 ; 変換テーブルベース制御(TTBCR)初期化 LDR r0,=||Image$$TTB$$ZI$$Base|| ; TLBベースアドレスを設定 MOV r1, #TTBR0_SETTING ; ORR r0,r0,r1 ; TTBR0の設定値を設定 MCR p15, 0, r0, c2, c0, 0 ; 変換テーブルベース0(TTBR0)に書き込み ;==================================================================== ; レベル1変換テーブル形式で作成(1Mbyteの仮想メモリ空間を4096個で制御します) ;==================================================================== LDR r0, =||Image$$TTB$$ZI$$Base|| ; TLBの先頭アドレスを設定 LDR r1, =0xFFF ; カウンタを初期化 LDR r2, =PAGE_TABLE_STRONGLY ; ページテーブルの初期設定 init_ttb_1 ORR r3, r2, r1, LSL#20 ; ベースアドレスを設定 STR r3, [r0, r1, LSL#2] ; TLBの書き込み SUBS r1, r1, #1 ; カウンタを-1 BPL init_ttb_1 ; エントリテーブル作成終了? ;==================================================================== ; ベクタ領域 ページテーブルエントリ設定します ;==================================================================== LDR r1, =||Image$$VECTORS$$Base|| ; TLBの先頭アドレスを設定 LSR r1, #20 ; 1MB境界にアドレスを設定 LDR r2, =PAGE_TABLE_NORMAL ; ノーマルメモリ ORR r3, r2, r1, LSL#20 ; TLBの初期値を求める STR r3, [r0, r1, LSL#2] ; ページテーブルの更新
ドメインアクセス制御の設定
ドメインアクセス制御レジスタの設定で、全クライアント設定に初期化します。詳しくは詳しくは「第12回:MMU」を参照ください。
;==================================================================== ; ドメインアクセス制御レジスタを全クライアント設定 ;==================================================================== LDR r0, =0x55555555 ; 全クライアント設定 MCR p15, 0, r0, c3, c0, 0 ; ドメインアクセス制御レジスタ(DACR)書き込み
NEON/VFPのアクセス許可と稼働設定
コプロセッサ(NEON/VFP)のアクセス権設定と稼働設定を行います。詳しくは「第13回:コプロセッサ設定例」を参照ください。
;==================================================================== ; NEON/VFPのアクセス許可を実施(CP10/CP11のアクセスを許可) ;==================================================================== MRC p15, 0, r0, c1, c0, 2 ; コプロセッサアクセス制御レジスタ(CPACR)読み込み ORR r0, r0, #(0xF ≪ 20) ; CP10/11のフルアクセス設定 MCR p15, 0, r0, c1, c0, 2 ; コプロセッサアクセス制御レジスタ(CPACR)書き込み ISB ; ;==================================================================== ; NEON/VFP動作開始 ;==================================================================== MOV r0, #0x40000000 ; VMSR FPEXC, r0 ; 浮動小数点例外レジスタでENビットを設定
MMUの稼働設定
MMUは、リセット時に非稼働に設定されていますので、すべての設定が終了後に有効設定します。MMU稼働設定直後に、アボート例外が発生した場合はTLB設定を確認します。詳しくは「第12回:MMU」を参照ください。
;==================================================================== ; MMUを稼働状態に設定します。 ;==================================================================== MRC p15,0,r0,c1,c0,0 ; SCTLRを読み込み ORR r0,r0,#0x1 ; MMUを稼働状態に設定(SCTLR.M[0]) MCR p15,0,r0,c1,c0,0 ; SCTLRに書き込み DSB ; 全てのメモリアクセスを終了 ISB ; 命令同期バリアの実行
ライブラリの初期化実行からmain()関数の呼び出し
初期化処理終了後に、処理系ライブラリを実行し、「システムモード(第5回参照)」でmain()関数を呼び出します。初期化処理は命令/データキャッシュは無効状態で実行しますので、命令/データキャッシュ、プログラムフロー予測、データプリフェッチの各機能を稼働します。その後、ペリフェラルの初期化、割り込みを許可し、必要に応じてプロセッサの動作モードをユーザモード(非特権)に変更します。
最後に
全17回のAPS ACADEMY Cortex-A入門編は、皆様の開発のお役に立ったでしょうか?開発を始める時の参考にしていただければ嬉しく思います。最後に、APS ACADEMY Cortex-A入門編の1年5か月間の執筆活動を支えていただきましたすべての方に感謝を申し上げます。職場のメンバーには、さまざまな協力をいただき感謝します。最後に、執筆の応援をしてくれた家族に感謝します。
2015年7月吉日
殿下信二
こちらも是非
“もっと見る” Cortex-A編
PMU(パフォーマンス監視ユニット)
PMUに関連するレジスタは、ユーザモードでのアクセスは禁止されていますので、PMUSERENR(ユーザイネーブルレジスタ)を特権モードでユーザモードアクセス許可を設定します。PMUSERENRについては、 後述の該当項目を参照ください。
TrustZone(セキュリティ拡張機能)
TrustZoneはCortex-Aシリーズの拡張機能で、大規模OSやアプリケーションが動作するノーマルワールドとセキュリティ関連が動作するセキュアワールドを導入しています。TrustZoneでは、ノーマルワールドメモリ空間とセキュアワールドメモリ空間の分離が可能です。
NEONコプロセッサ
NEONコプロセッサは、リセット時に無効化されるため、初期化処理でアクセス権設定と稼働設定が必要です。NEONコプロセッサが無効状態でNEON命令を実行した場合、「未定義命令例外」が発生します。