前面學習了選擇從那裏進行加載代碼,接着下來,咱們將要了解CPU是怎麼樣運行編譯的代碼。經過前面的學習,咱們知道經過編譯器的編譯,會生成可運行的代碼,而後經過JLINK下載到STM32的FLASH裏,再經過配置CPU的引導管腳,實現選擇內部FLASH來加載代碼。可是CPU是怎麼樣來加載代碼,並運行的呢?佈局
其實不一樣類型的CPU運行代碼的方式不同,STM32的運行方式是這樣的,CPU 將從地址 0x0000 0000 獲取棧頂值,而後從始於 0x0000 0004 的自舉存儲器開始執行代碼。前面已經把0x800 0000的FLASH地址配置爲啓動的存儲器,所以就會從這裏偏移位置0取得棧頂值,從偏移位置4取得第一行執行代碼。根據這個要求,那麼每一個項目工程的啓動文件,必須是這樣設置才能夠運行。來看工程裏的startup_stm32f40_41xxx.s文件,這個文件就是STM32F407運行的第一個文件,它編譯出來的代碼,就是被第一行執行的,所以它是採用彙編語言來編寫的。也許有人問爲何不採用C語言來編寫,非要使用匯編語言來編寫?其實還真不能使用C語言來編寫,由於CPU運行時,根本沒有具有C語言的運行環境,因此不能直接運行C語言編譯出來的代碼,必須經過彙編語言來創建C語言的運行環境才能夠運行C語言編譯出來的代碼。這個彙編語言文件是庫文件裏的一部分,它的目錄路徑是學習
Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm\startup_stm32f40_41xxx.s。.net
這個文件裏的內容比較多,而且是彙編語言,要看懂它,須要下些功夫才能夠。前面說到CPU會從偏移位置0處來加載棧頂值,那麼在這個彙編文件裏是怎麼樣實現把棧頂的值放到偏移位置0的呢?如今來打開這個文件,查看到以下圖:blog
能夠從這裏看到__initial_sp變量放在第一個位置,意味着這個變量的值會放在這個區域的第一個位置,這樣確保了棧頂值保存在這段代碼佈局的首位置了。可是你也許問編譯器怎麼樣知道這段代碼必定會放在首位置,而不是放置在存儲器的後面位置呢?若是沒有的特別的聲明,編譯器還真可能把它放置在任何位置上的,並非首位置。關鍵之處,是項目裏還有一個編譯鏈接的說明文件,它叫作project.sct,這個文件主要用來指定ARM鏈接器在生成映像文件時如何分配RO,RW,ZI等數據的存放地址,在個人工程裏採用默認的文件,內容以下:ip
; *************************************************************編譯器
; *** Scatter-Loading Description File generated by uVision ***it
; *************************************************************io
LR_IROM1 0x08000000 0x00080000 { ; load region size_region編譯
ER_IROM1 0x08000000 0x00080000 { ; load address = execution address變量
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00020000 { ; RW data
.ANY (+RW +ZI)
}
}
在這裏看到,加載代碼和執行代碼的地址,就是前面設置FLASH的地址,緊接下來會看到下面這行:
*.o (RESET, +First)
這行的意思就是說,把全部目標文件.o結尾的代碼,都放置在這裏,可是要把標識爲RESET段的代碼放置在最前面,所以這裏明確地說明RESET的代碼放在首位置,就是偏移0的位置。那麼回過頭來看一下啓動的彙編文件,以下:
在這裏看到AREA後面緊跟着一個RESET名稱,也就是說這段代碼區域就被命名爲RESET了,當編譯器看到這個名稱,就會把這段代碼放置在存儲器的首位置,這樣CPU就會從這裏得到棧頂值,就能夠開始加載代碼進行運行了,從偏移4的位置,也就是Reset_Handler的值,把它放到CPU的執行指令寄存器,就進入代碼運行了。
https://blog.csdn.net/caimouse/article/details/51749579 ———————————————— 版權聲明:本文爲CSDN博主「caimouse」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/caimouse/article/details/93863062