# Lab 0
###### tags: `MPSL` `NCTU`
這次 lab 主要就是熟悉整個開發環境。
> 實驗 Report 主要紀錄自己實驗的過程,或是有甚麼想分享的經驗,也不需要**硬寫很多**。
> 把實驗 Report 當作是自己上過這門課的 footprint。
> 繳交方式為 pdf 或網址 (HackMD, Dropbox Paper...)
>
## 建立開發環境
* 使用 eclipse based STM32 IDE tool 做為開發工具。
* 開發環境為 win10,可以由[官網](https://www.openstm32.org/Downloading+the+System+Workbench+for+STM32+installer)的[下載連結](http://www.ac6-tools.com/downloads/SW4STM32/install_sw4stm32_win_64bits-latest.exe)獲取安裝包。
* 基本上按照流程就會完成所有的安裝,包括 ST-Link driver。
* 安裝地點預設為:C:\Ac6\SystemWorkbench
* 安裝完成後,即可點擊 **System Workbench for STM32** 開始寫第一份 code。
* 這裡創了一個 MCSL (C:\Users\ieros\MCSL) 作為之後 lab 的 workspace,第一次開啟時,會自動安裝 arm 的 toolchain。
* File -> New -> Other,選擇 C Project
* 
* 設定好新的 Project name,並選擇 Ac6 STM32 MCU Project 作為我們的 Project Type
* 
* 這裡預設就會把 Debug 打開。
* 
* 板子一定要選擇硬體對應的編號,才能在之後燒錄時,將程式放到記憶體中正確的位置。以我們的的板子來說,就要選擇 STM32L4 和 NUCLEO-L476RG。
* 
## 第一支程式
* File -> New -> Source File
* Source file: main.s
* Code
```cpp=
.syntax unified
.cpu cortex-m4
.thumb
.text
.global main
.equ AA, 0x5566
main:
movs r0, #AA
movs r1, #20
adds r2, r0, r1
L: B L
```
* Project -> Build All
* 這裡發生編譯問題,問題出在 ```movs r0, #AA```,但如果將 ```.equ AA, 0x5566``` 改成 ```.equ AA, 0x1100``` 就會成功了!
* 
* Run -> Run 即可在硬體執行程式。
### Debug
* Run -> Debug 可以透過 IDE 進行 Debug,並觀察硬體上每個 Register 的變化。
* 按下 Debug 之後,可以按 F5 進行單步執行,或是按兩下 F8 執行全部。
* 觀察 Register 變化前,必須先按下暫停。在右上方的視窗中,我們可以看到 r0 已經變成 0x1100 了!
* 
* Object Dump 可以協助我們瀏覽程式的 symbol table。
* Run -> External Tools -> External Tools Configuration
* New launch configuration
* 
## 第二支程式
* Code
```cpp=
.syntax unified
.cpu cortex-m4
.thumb
.data
X: .word 100
Y: .asciz "Hello World!"
.text
.global main
.equ AA, 0x55
main:
ldr r1, =X
ldr r0, [r1]
movs r2, #AA
adds r2, r2, r0
str r2, [r1]
ldr r1, =Y
ldr r2, [r1]
L: B L
```
* 這隻程式在 data section 的部分宣告了 X 與 Y 兩個變數。
* 主程式的部分
1. 先透過 ldr 將 X 的記憶體位置讀進 r1
* 可以從右上角的 Register 看到,r1 的值為 0x20000000。
* 
* 透過下方的 Memory monitors,0x20000000 位置的值為 0x64,也就是 100。
* 
> 每個記憶體位置都是一個 8 bits (1 byte) 的資料空間,
> 因為宣告時是給 X 一個 word (4 bytes) 的大小 (從 0x20000000 到 0x20000004),因此 Y 是從記憶體位置 0x20000004 開始放。
* 也可以透過 New Renderings 來新增一個以 Signed Integer 為表示方式的 Monitor
* 
2. 將 r1 中記憶體位置的資料透過 ldr 讀進 r0
* 這時候,r0 的值就會變成 100 了。
3. 接下來兩行作了一個加法的運算,並將結果存在 r2 中,r2 為 185。
* 再透過 str 將 r2 的值存進 r1 所記錄的記憶體位置之中
* 此時 0x20000000 的值變為 185 了。
* 
4. 最後再將 Y 的值透過一樣的方式,存進 r2 中。
* 我們也可以透過 memory monitor 看到,0x20000004 到 0x20000010 之間,存了 12 個 bytes。
* 
## 第三支程式
* 簡單的四則運算,並將計算結果存回記憶體之中。
* Code
```cpp=
.syntax unified
.cpu cortex-m4
.thumb
.data
X: .word 5
Y: .word 10
Z: .word 0
.text
.global main
main:
ldr r0, =X
ldr r1, =Y
ldr r2, =Z
ldr r3, [r0]
ldr r4, [r1]
ldr r5, [r2]
movs r6, #10
muls r3, r3, r6 // X = X * 10
adds r3, r3, r4 // X = X + Y
subs r5, r4, r3 // Z = Y - X
str r5, [r2]
L: B L
```
* 執行結果
* 
## startup_stm32.s 與 LinkerScript.ld
* 在 LinkerScript.ld 中定義了 entry point 為 Reset_Handler
```cpp=
/* Entry Point */
ENTRY(Reset_Handler)
```
* 因此每次系統啟動時,都會透過 Reset_Handler 來對 data section 作初始化,從 flash 將資料搬進 SRAM 之中,並且對 bss section 作填零的動作。
```cpp=
Reset_Handler:
ldr r0, =_estack
mov sp, r0 /* set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
ldr r0, =_sdata
ldr r1, =_edata
ldr r2, =_sidata
movs r3, #0
b LoopCopyDataInit
CopyDataInit:
ldr r4, [r2, r3]
str r4, [r0, r3]
adds r3, r3, #4
LoopCopyDataInit:
adds r4, r0, r3
cmp r4, r1
bcc CopyDataInit
/* Zero fill the bss segment. */
ldr r2, =_sbss
ldr r4, =_ebss
movs r3, #0
b LoopFillZerobss
FillZerobss:
str r3, [r2]
adds r2, r2, #4
LoopFillZerobss:
cmp r2, r4
bcc FillZerobss
```
* 最後再跳到 main 開始執行我們所寫的 code
```cpp=
/* Call the application's entry point.*/
bl main
```
* 以第三個程式為例,由 Symbol Table 可以看到,X、Y 與 Z 會先被放在 data section 位置。
* 
* 接著開始執行 startup 後,再將資料從 flash 搬進 SRAM 之中。
* 
## data section 與 text section
* 最明顯的差異在於,data section 是可讀可寫的,而 text section 只可以讀不可以寫
* text section 是儲存在 Rom 之中,而 data section 則是放在 Ram。
* 紀錄在 linker script 之中
```cpp=
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
ROM (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}
```
```cpp=
/* The program code and other data into ROM memory */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >ROM
```
```cpp=
/* Initialized data sections into RAM memory */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> ROM
```
* 如果將 X 改宣告在 .text 之下,可以發現,X 的位置變成了 0x80001c8,這個位置是在 text section,並且執行到最後可以發現,0x80001c8 位置的值並不會因為 ldr 將 185 存回去而被改變。
## Little Endian
* 可以觀察 ```Y: .asciz "Hello World!"``` 是怎麼被放入記憶體的:
* 在 Register 中我們可以看到,r2 的值是 0x6c6c6548,也就是 ASCII 的 "lleH",然而如果用 Hex 模式去看 memory monitor,可以發現 0x20000004 位置中的值為 0x48656C6C。
* 這是因為 memory 是 Little Endian 系統,因此最低位元組會被擺在 memory 位址的最低位 (最左邊),而最高位元組則被放在 memory 位址的最高位 (最右邊)。
* 
> 根據 ARM Cortex L4 technical reference manual,ARM 架構其實是支援 big/little endian,但意法半導體選擇使用 little endian。
## 字串的宣告方法
* .asciz
* 會自動的結尾補 '\0'
* .ascii
* 不會在結尾補 null byte