MCU變數載入過程
- 2021 年 12 月 29 日
- 筆記
- -- 個人理解,有錯歡迎討論(Air-Liu), 單片機相關應用
前言
在開發mcu程式碼的時候經常會有些疑惑,變數是怎麼在編譯之後進入單片機的ram區的呢,特別是在使用keil開發的時候。後來在接觸gcc編譯器和自研的mcu後,終於明白了這個問題。實際上變數編譯後被放在了bin文件中程式碼的後面。程式運行時會主動將該區域的數據依次搬運到ram區域中。
原理
寫完程式碼編譯後,會把code中的所有變數放在程式碼的後面,當然這個規則也是由鏈接文件來決定的。示例圖如下
在程式運行後正式進入C環境下,code中會存在一段彙編程式碼。主要作用就是把data區域記憶體依次複製到ram中,複製結束後把後面和bss長度一致區域的記憶體全置為0。
通過這也能夠看出為什麼bss區域的值會被設置為0,因為記憶體中是沒有類型的說法。如果不全置0,無論其它哪個值都沒法確認實際類型的值。
但是在keil中我們沒有看到這樣的程式碼。keil的彙編程式碼如下:
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
這段程式碼執行完畢後會跳轉到__main函數里執行,主要注意的是這裡並不是直接跳轉到我們編寫的main函數里。在__main有很多的操作,其中就包括將變數複製到ram區域中。執行完畢後才會跳轉到我們自己開發的main函數中執行。
示例分析
下面這段程式碼就是gcc編譯器下移動變數記憶體的程式碼。其中有幾個變數,data區的起始、結束地址,bss區的起始、結束地址都是通過ld文件中獲取的。
運行完下面的code後,程式中的所有變數都被移動到了ram區域中。
/*
* The ranges of copy from/to are specified by following symbols
* __etext: LMA of start of the section to copy from. Usually end of text
* __data_start__: VMA of start of the section to copy to
* __data_end__: VMA of end of the section to copy to
*
* All addresses must be aligned to 4 bytes boundary.
*/
lrw r1, __erodata // data在bin文件中的起始地址
lrw r2, __data_start__ // data在ram中的起始地址
lrw r3, __data_end__ // data在ram中的結束地址
subu r3, r2 // r3為data的長度
cmpnei r3, 0 // 判斷長度是否為0
bf .L_loop0_done
.L_loop0: // 將bin文件中數據依次移到ram中
ldw r0, (r1, 0)
stw r0, (r2, 0)
addi r1, 4
addi r2, 4
subi r3, 4
cmpnei r3, 0
bt .L_loop0
.L_loop0_done:
/*
* The BSS section is specified by following symbols
* __bss_start__: start of the BSS section.
* __bss_end__: end of the BSS section.
*
* Both addresses must be aligned to 4 bytes boundary.
*/
lrw r1, __bss_start__
lrw r2, __bss_end__
movi r0, 0
subu r2, r1
cmpnei r2, 0
bf .L_loop1_done
.L_loop1: // 將bss里的數據複製為0
stw r0, (r1, 0)
addi r1, 4
subi r2, 4
cmpnei r2, 0
bt .L_loop1
.L_loop1_done: