# 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 * ![](https://i.imgur.com/X6eVtPs.png) * 設定好新的 Project name,並選擇 Ac6 STM32 MCU Project 作為我們的 Project Type * ![](https://i.imgur.com/K32fJml.png) * 這裡預設就會把 Debug 打開。 * ![](https://i.imgur.com/5nSgSJx.png) * 板子一定要選擇硬體對應的編號,才能在之後燒錄時,將程式放到記憶體中正確的位置。以我們的的板子來說,就要選擇 STM32L4 和 NUCLEO-L476RG。 * ![](https://i.imgur.com/QJYDwl8.png) ## 第一支程式 * 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``` 就會成功了! * ![](https://i.imgur.com/RnFEqsb.png) * Run -> Run 即可在硬體執行程式。 ### Debug * Run -> Debug 可以透過 IDE 進行 Debug,並觀察硬體上每個 Register 的變化。 * 按下 Debug 之後,可以按 F5 進行單步執行,或是按兩下 F8 執行全部。 * 觀察 Register 變化前,必須先按下暫停。在右上方的視窗中,我們可以看到 r0 已經變成 0x1100 了! * ![](https://i.imgur.com/u8WyECw.png) * Object Dump 可以協助我們瀏覽程式的 symbol table。 * Run -> External Tools -> External Tools Configuration * New launch configuration * ![](https://i.imgur.com/rAjLzQ7.png) ## 第二支程式 * 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。 * ![](https://i.imgur.com/RTqNTY2.png) * 透過下方的 Memory monitors,0x20000000 位置的值為 0x64,也就是 100。 * ![](https://i.imgur.com/rwSCam5.png) > 每個記憶體位置都是一個 8 bits (1 byte) 的資料空間, > 因為宣告時是給 X 一個 word (4 bytes) 的大小 (從 0x20000000 到 0x20000004),因此 Y 是從記憶體位置 0x20000004 開始放。 * 也可以透過 New Renderings 來新增一個以 Signed Integer 為表示方式的 Monitor * ![](https://i.imgur.com/jxOFDHO.png) 2. 將 r1 中記憶體位置的資料透過 ldr 讀進 r0 * 這時候,r0 的值就會變成 100 了。 3. 接下來兩行作了一個加法的運算,並將結果存在 r2 中,r2 為 185。 * 再透過 str 將 r2 的值存進 r1 所記錄的記憶體位置之中 * 此時 0x20000000 的值變為 185 了。 * ![](https://i.imgur.com/1FAT5oi.png) 4. 最後再將 Y 的值透過一樣的方式,存進 r2 中。 * 我們也可以透過 memory monitor 看到,0x20000004 到 0x20000010 之間,存了 12 個 bytes。 * ![](https://i.imgur.com/gPsCmfz.png) ## 第三支程式 * 簡單的四則運算,並將計算結果存回記憶體之中。 * 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 ``` * 執行結果 * ![](https://i.imgur.com/tp51j5K.png) ## 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 位置。 * ![](https://i.imgur.com/tKZRVU1.png) * 接著開始執行 startup 後,再將資料從 flash 搬進 SRAM 之中。 * ![](https://i.imgur.com/7bMg0dT.png) ## 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 位址的最高位 (最右邊)。 * ![](https://i.imgur.com/q6pPIZV.png) > 根據 ARM Cortex L4 technical reference manual,ARM 架構其實是支援 big/little endian,但意法半導體選擇使用 little endian。 ## 字串的宣告方法 * .asciz * 會自動的結尾補 '\0' * .ascii * 不會在結尾補 null byte