初期化処理

初期化処理

Cortex-A9シングルプロセッサのリセット例外から、main()関数までの動作を解説します。「セキュアワールド特権(第15回参照)で動作し、メモリ空間は0x80000000番地から1MバイトのRAM空間をノーマルメモリ・キャッシュ可能に設定し、他の領域は「ストロングリオーダメモリ(第10回参照)」に設定します。仮想アドレスと物理アドレスは、同一アドレスとして「MMU(第12回参照)」の設定をします。

リセット例外からmain()関数を呼び出すまでの初期化は、ユーザが作成する部分(ユーザーコード)とARMコンパイラが実行する部分(処理系ライブラリ)に分けることができます。コードのコピーや初期化変数/未初期化変数の初期化は、リンカのメモリ配置設定(スキャッタローディング記述ファイル設定)を処理系ライブラリが実行します。

図1

例外ベクタテーブル

例外ベクタテーブルには、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    fiq_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()関数を呼び出します。ARMコンパイラの初期化処理は、命令/データキャッシュは無効状態で実行しますので、命令/データキャッシュ、プログラムフロー予測、データプリフェッチの各機能を稼働します。その後、ペリフェラルの初期化、割り込みを許可し、必要に応じてプロセッサの動作モードをユーザモード(非特権)に変更します。

最後に

全17回のAPS ACADEMY Cortex-A入門編は、皆様の開発のお役に立ったでしょうか?開発を始める時の参考にしていただければ嬉しく思います。最後に、APS ACADEMY Cortex-A入門編の1年5か月間の執筆活動を支えていただきましたすべての方に感謝を申し上げます。職場のメンバーには、さまざまな協力をいただき感謝します。最後に、執筆の応援をしてくれた家族に感謝します。

2015年7月吉日
殿下信二