STM32 Nucleo Boardスタートアップルーチン

#STM32 Nucleo Boardスタートアップルーチン
[RaspberryPiでSTM32の開発環境構築](https://qiita.com/mitazet/items/bfa944c0583b88cf486f)の環境を用いてSTM32の開発を行う。
私の知識ではスタートアップルーチンをスクラッチで書くことはできないため、
githubからソースコードを入手した。学習のため中身を詳しく読んでみることにした。

##開発ターゲット
STM32 Nucleo Board STM32F303K8

##参照したソースコード
[STM32-Communication-Test/startup/startup_stm32f303x8.s](https://github.com/walopes/STM32-Communication-Test/blob/master/startup/startup_stm32f303x8.s)

##参考サイト
[Using as](https://sourceware.org/binutils/docs-2.24/as/index.html#Top)

##解説
ソースの先頭から読んでみる。

```asm
.syntax unified   /* 統合アセンブリ構文によって書かれている*/
.cpu cortex-m4    /* cortex-m4 */
.fpu softvfp      /* ソフトウェア浮小数点リンケージ指定 */
.thumb            /* 後続の命令をT32として解釈 */

.global	g_pfnVectors
.global	Default_Handler
```
最初にこのアセンブリ構文についての設定を行なっている。
`.global`で`g_pfnVectors`、`Default_Handler`ラベルを他のオブジェクトファイルでも利用可能なように公開している。

```asm
.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関数を呼び出す。

```asm
    .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のアドレスに設定している。

```asm
  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領域にコピーする処理を行う。

```asm
/* 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埋めしている。

```asm
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の定義となる。
これは割り込み処理のデフォルト動作としている。

```asm
   .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では命令を記述する必要は無く、飛び先アドレスを記述すればハンドラが自動的にそのアドレスに飛ばしてくれる。

```asm
.section	.isr_vector,"a",%progbits
	.type	g_pfnVectors, %object
	.size	g_pfnVectors, .-g_pfnVectors
```
ここでは割り込みベクタテーブルのアセンブルを指示している。
`.section`で.isr_vectorセクションをアセンブルするように指示。
`.type`はg_pfnVectorsがデータオブジェクトであること示す。

```asm
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)が割り当てられている。

```asm
  .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のスタートアップルーチン。