#STM32 Nucleo Boardスタートアップルーチン
RaspberryPiでSTM32の開発環境構築の環境を用いてSTM32の開発を行う。
私の知識ではスタートアップルーチンをスクラッチで書くことはできないため、
githubからソースコードを入手した。学習のため中身を詳しく読んでみることにした。
##開発ターゲット
STM32 Nucleo Board STM32F303K8
##参照したソースコード
STM32-Communication-Test/startup/startup_stm32f303x8.s
##参考サイト
Using as
##解説
ソースの先頭から読んでみる。
.syntax unified /* 統合アセンブリ構文によって書かれている*/
.cpu cortex-m4 /* cortex-m4 */
.fpu softvfp /* ソフトウェア浮小数点リンケージ指定 */
.thumb /* 後続の命令をT32として解釈 */
.global g_pfnVectors
.global Default_Handler
最初にこのアセンブリ構文についての設定を行なっている。
.global
でg_pfnVectors
、Default_Handler
ラベルを他のオブジェクトファイルでも利用可能なように公開している。
.word _sidata
.word _sdata
.word _edata
.word _sbss
.word _ebss
.equ BootRAM, 0xF1E0F85F
_sidata/_sdata/_edata/_sbss/_ebss
はリンカスクリプトで定義されている値。
.equ
ディレクティブを使うとこのファイル中ではBootRAM
という文字列が0xF1E0F85F
に置き換えられる。
ここから先はReset_Hander関数となる。
Reset_Handlerはリセットイベントでプロセッサが最初に実行する処理で、
各種初期設定を行なった後にmain関数を呼び出す。
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* Atollic update: set stack pointer */
ここではReset_Handerのアセンブルを指示し、最初の処理を記述している。
.section
は次のような形式で新しいセクションをアセンブルするようにアセンブラに指示する。
.section name [, "flags"[, @type[,flag_specific_arguments]]]
flagsはセクションに関する情報を提供。使用できるセクションフラグは以下の通り。
-aは、セクションが割り当て可能
-xは、セクションが実行可能
-wは、セクションが書き込み可能
-sは、セクションに NULL で終わる文字列が含まれていること
.weak
は他のソースで定義されていない場合のみ公開するという意味。
.type ..., %function
はReset_Handlerが関数名であることを示す。
コードでは.textの.Reset_Handlerセクションをアセンブルするように指示。
最初にスタックポインタの値を_estackのアドレスに設定している。
movs r1, #0
b LoopCopyDataInit
/* r1を0で初期化してLoopCopyDataInitにジャンプ */
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
/* _sidata+r1(idata)の内容を_sdata+r1(data)領域にコピーし、r1を次のアドレスに進める */
LoopCopyDataInit:
ldr r0, =_sdata /* r0は開始アドレス */
ldr r3, =_edata /* r3は終了アドレス */
adds r2, r0, r1 /* r2はコピー済みアドレス */
cmp r2, r3 /* r2とr3を比較 */
bcc CopyDataInit /* r2が終了アドレスに達するまでコピーを繰り返す*/
ldr r2, =_sbss /* r2に_sbssのアドレスを代入 */
b LoopFillZerobss /* LoopFillZerobssにジャンプ */
ここではDataセクションをRAMにコピーしている。
Dataセクションは初期値を持つ大域変数の領域。
ROMに格納されているが定数ではないためRAMにコピーする必要がある。
LoopCopyDataInitはループの終了と継続の制御。
CopyDataInitは_sidata領域のデータを_sdata領域にコピーする処理を行う。
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0 /* r3に0を代入 */
str r3, [r2], #4 /* r2の示すアドレスにr3(=0)を代入しr2を次のアドレスへ進める*/
LoopFillZerobss:
ldr r3, = _ebss /* r3に終了アドレスを代入 */
cmp r2, r3 /* r2とr3を比較 */
bcc FillZerobss /* r2が終了アドレスに達するまで0埋めを繰り返す */
ここではbssセクションの初期化を行なっている。
bssは初期値なし大域変数、静的局所変数のセクション。
C言語の規約では、「この領域はすべて0で初期化されなければならない」と規定されているため、0埋めしている。
bl SystemInit
bl __libc_init_array
bl main
LoopForever:
b LoopForever
.size Reset_Handler, .-Reset_Handler
ここではmain関数の呼び出しを行う。
まず、SystemInit関数でシステムを初期化。
その後main関数を呼び出しメインプログラムに移行する。
main関数から返った場合は無限ループとなる。
.size
でReset_Handlerのラベルからサイズを設定している。
ここから先はDefault_Handlerの定義となる。
これは割り込み処理のデフォルト動作としている。
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
.secrion
で.textの.Default_Handlerセクションをアセンブルするように指示。
“ax”フラグは実行可能、%progbitsはdataを含むセクションであることを示す。
動作は無限ループ。
ここから先は割り込みベクタテーブルとなる。
STM32の割り込み要因に対して割り込みサービスルーチンを用意する。
割り込み要因に対応する割り込みサービスルーチンの先頭アドレスを
まとめたものが割り込みベクタテーブル。
ベクタテーブルは0番地から始まる。テーブル内には、例外ハンドラとISRのアドレスを記述する。
他のARMプロセッサでは、命令を記述し、その命令を実行して飛び先アドレスにジャンプする方式だが、
Cortex-M4では命令を記述する必要は無く、飛び先アドレスを記述すればハンドラが自動的にそのアドレスに飛ばしてくれる。
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
ここでは割り込みベクタテーブルのアセンブルを指示している。
.section
で.isr_vectorセクションをアセンブルするように指示。
.type
はg_pfnVectorsがデータオブジェクトであること示す。
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
:
中略
:
.word 0
.word FPU_IRQHandler
ここでテーブルを記述している。
一般的なマイコンは、ベクタテーブルの最小アドレス部(0番地)にはリセットベクタが割り当てられているが、
Cortex-M4ではスタックポインタの初期値(_estack)が割り当てられている。
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
:
中略
:
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
ここではそれぞれのハンドラの動作を定義している。
他で処理が定義されない場合にはDefault_Handlerが使用される。
以上がSTM32 Nucleo Boardのスタートアップルーチン。