你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們介紹的是飛思卡爾Kinetis系列MCU的KBOOT形態。html
痞子衡在前一篇文章裏簡介了 KBOOT架構,咱們知道KBOOT是一個完善的Bootloader解決方案,這個解決方案主要設計用於Kinetis芯片上,目前Kinetis芯片起碼有上百種型號,KBOOT在這上百種Kinetis芯片裏存在的形式並非徹底同樣的,KBOOT主要有三種存在形式(ROM Bootloader、Flashloader、Flash-Resident Bootloader),下面痞子衡爲你們細說這三種形態:python
KBOOT有三種形態,分別是以下圖所示的ROM Bootloader、Flashloader、Flash-Resident Bootloader,三種形態共享大部分KBOOT源碼,僅在一些細節上有差異,這些細節在KBOOT源碼裏是用條件編譯加以區分的,對應的條件編譯宏分別是BL_TARGET_ROM, BL_TARGET_RAM, BL_TARGET_FLASH。三種形態最大的區別實際上是在連接文件上,通過彙編器後的read only section分別連接在了Kinetis芯片System memory空間裏的ROM(起始地址0x1c000000)、RAM(區間地址0x20000000)、Flash(起始地址0x00000000)區域。shell
下表是KBOOT三種形態的對比,分別從use case、delivery mechanism、supported device、clock configuration、feature五大角度進行了對比:編程
總結來講,能夠這麼看KBOOT這三種存在的由來:架構
- 對於2014年初及之後問世的Kinetis芯片(好比MKL0三、MKL2七、MKL4三、MKL80、MKE18F等),芯片內基本都是含ROM空間的,所以KBOOT是以ROM Bootloader的形式存在的;
- 對於2014年初及之後主推的Kinetis芯片(好比MK2二、MK6五、MKV3一、MKS22等),芯片內雖然沒有ROM空間,但飛思卡爾但願能給客戶提供至少一次免編程器燒錄Application(用於量產)的機會,所以KBOOT是以Flashloader的形式存在的;
- 對於在市場上主流又暢銷的Kinetis芯片(好比MKL2五、MK2二、MK6六、MKL28等),無論芯片內是否有ROM空間,飛思卡爾都但願可以給出Bootloader源碼,以便讓客戶自由修改來知足其個性化需求,所以KBOOT是以Flash-Resident Bootloader的形式存在的;
KBOOT的ROM Bootloader形態是放在ROM空間裏的,隨着芯片一塊兒Tape-out出廠,固化在芯片裏面,因此該形態能夠被當作硬件模塊,能夠被無限次使用。
由於有了ROM的存在,因此芯片上電啓動便有了兩種選擇:從ROM啓動、從內部Flash啓動,這種啓動選擇是由芯片系統決定的。
若是是從ROM啓動,那麼咱們能夠藉助ROM將Application燒寫進Flash(內部/外部)的起始空間並跳轉過去執行。跳轉至Flash執行分爲:從內部Flash執行、從外部QSPI NOR Flash執行,這種執行選擇是由ROM代碼決定的。
若是已經使用ROM將Application下載進內部Flash起始地址,並在系統設置裏設置芯片從內部Flash啓動,那麼下次芯片復位啓動徹底能夠繞開ROM直接從內部Flash起始地址執行Application。ide
KBOOT的Flash-Resident Bootloader形態是放在內部Flash起始空間的,以源代碼的形式提供給客戶,客戶須要本身編譯KBOOT工程並使用編程器/調試器將編譯生成的KBOOT binary下載進芯片內部Flash起始地址,除非使用調試器將其擦除,不然其也能夠被無限次使用。
對於沒有ROM的芯片,芯片上電只能從內部Flash起始地址處開始啓動,由於Flash-Resident Bootloader已經佔據了內部Flash的起始空間,因此芯片永遠是先執行Flash-Resident Bootloader。藉助Flash-Resident Bootloader只能將Application燒寫進內部Flash必定偏移處(這個偏移地址由Flash-Resident Bootloader指定)並跳轉過去執行。函數
KBOOT的Flashloader形態其實也是放在內部Flash起始空間的,不過與Flash-Resident Bootloader形態在Flash裏執行不一樣之處在於Flashloader形態是在SRAM裏執行的,衆所周知,SRAM斷電是不保存數據的,所以Flashloader須要一個放在內部Flash裏的配套loader程序,在芯片上電時先運行Flash裏的loader程序,由loader程序將Flashloader從Flash中搬運到SRAM中並跳轉到SRAM中運行。
Flashloader是在芯片出廠以後由飛思卡爾產品工程師將其binary預先下載進內部Flash再售賣給客戶,因此客戶拿到芯片以後至少可使用一次Flashloader,客戶藉助Flashloader能夠將Application燒寫進內部Flash起始空間(同時也覆蓋了原Flashloader-loader),這就是Flashloader只能被使用一次的緣由。工具
關於loader機制與實現,有必要詳細講解一下,讓咱們結合代碼分析,首先從官網下載NXP_Kinetis_Bootloader_2_0_0.zip包,就以KS22芯片爲例(\targets\MKS22F25612\bootloader.eww):ui
使用IAR EWARM 7.80.x開發環境打開KS22的Bootloader工程,能夠看到有以下三個工程,其中flashloader.ewp即是主角,其源碼文件與ROM和Flash-Resident Bootloader是同樣,只是工程連接文件有區別,其代碼段連接在於SRAM裏;maps_bootloader.ewp即是Flash-Resident Bootloader,不是此處討論的重點;flashloader_loader.ewp就是Flashloader能在SRAM裏執行的關鍵所在。debug
flashloader_loader.ewp工程裏除了必要的芯片startup文件外,只有三個源文件:flashloader_image.c/h,bl_flashloader.c,其中bl_flashloader.c裏包含了工程main函數,讓咱們試着分析這個文件以及main函數,下面是bl_flashloader.c的文件內容:
#include <string.h> #include "bootloader_common.h" #include "fsl_device_registers.h" #include "bootloader/flashloader_image.h" #if DEBUG #include "debug/flashloader_image.c" #else #include "release/flashloader_image.c" #endif //////////////////////////////////////////////////////////////////////////////// // Code //////////////////////////////////////////////////////////////////////////////// // @brief Run the bootloader. void bootloader_run(void) { // Copy flashloader image to RAM. // 關鍵拷貝,實現了flashloader binary從Flash到RAM的轉移 memcpy((void *)g_flashloaderBase, g_flashloaderImage, g_flashloaderSize); // Turn off interrupts. __disable_irq(); // Set the VTOR to default. SCB->VTOR = 0x0; // Memory barriers for good measure. __ISB(); __DSB(); // Set main stack pointer and process stack pointer. __set_MSP(g_flashloaderStack); __set_PSP(g_flashloaderStack); // Jump to flashloader entry point, does not return. // 關鍵跳轉,執行位置從Flash切換到了RAM void (*entry)(void) = (void (*)(void))g_flashloaderEntry; entry(); } // @brief Main bootloader entry point. int main(void) { bootloader_run(); // Should never end up here. while (1); }
從上述bl_flashloader.c的文件裏咱們能夠看到,其實loader工程的main函數特別簡單,它就是將內部Flash裏的g_flashloaderImage[]數據(即flashloader.ewp編譯生成的binary)拷貝到g_flashloaderBase地址(即flashloader binary起始地址)處,並將SP和PC分別指向g_flashloaderStack(即flashloader初始SP)和g_flashloaderEntry(即flashloader的Reset Handler入口)。
那麼g_flashloaderXX常量都放在哪裏的呢?打開\targets\MKS22F25612\iar\flashloader\output\Release\flashloader_image.c能夠找到答案:
const uint8_t g_flashloaderImage[] = { 0x70, 0x62, 0x00, 0x20, 0x11, 0xc4, 0xff, 0x1f, 0xeb, 0xc4, 0xff, 0x1f, 0x75, 0xef, 0xff, 0x1f, // 此處省略41552 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; const uint32_t g_flashloaderSize = 41584U; const uint32_t g_flashloaderBase = 0x1fffc000; const uint32_t g_flashloaderEntry = 0x1fffc411; const uint32_t g_flashloaderStack = 0x20006270;
loader機制愈來愈清晰了,如今只剩最後一個問題了,flashloader_image.c文件是哪裏來的?這個文件固然能夠手動建立,文件裏的信息均可以從flashloader.ewp工程生成的elf/map文件裏中找到,但本着高效的原則,但凡能腳本自動生成的決不手動建立,是的這個flashloader_image.c文件就是腳本自動生成的,在flashloader.ewp的Option選項的Build Actions裏能夠看到調用腳本的命令,這個腳本名叫create_flashloader_image.bat。
在\bin目錄下存放了全部腳本文件,固然也包括create_flashloader_image.bat,先打開這個腳本看一下:
cd /d %1 ielftool --bin output\%2\flashloader.elf flashloader.bin python ..\..\..\..\bin\create_fl_image.py output\%2\flashloader.elf flashloader.bin output\%2\flashloader_image.c
ielftool.exe是IAR軟件目錄下的工具,能夠將elf文件轉換成bin文件。最核心的腳本實際上是create_fl_image.py,這個python腳本根據elf文件和bin文件生成了flashloader_image.c文件。打開create_fl_image.py文件以下(做了一些異常判斷的刪減,爲了突出腳本主邏輯):
import sys import os import elf # usage: create_fl_image.py <elffile> <binfile> <cfile> def main(argv): # Collect arguments elfFilename = argv[0] binFilename = argv[1] cFilename = argv[2] # Open files binFile = open(binFilename, 'rb') cFile = open(cFilename, 'w') # 建立了elfData對象,用於後續處理.elf格式文件 elfData = elf.ELFObject() with open(elfFilename, 'rb') as elfFile: # 開始處理輸入的.elf文件 elfData.fromFile(elfFile) if elfData.e_type != elf.ELFObject.ET_EXEC: raise Exception("No executable") # 開始從.elf裏獲取關鍵信息 resetHandler = elfData.getSymbol("Reset_Handler") vectors = elfData.getSymbol("__Vectors") stack = elfData.getSymbol("CSTACK$$Limit") # Print header print >> cFile, 'const uint8_t g_flashloaderImage[] = {' # Print byte data totalBytes = 0 while True: data = binFile.read(16) dataLen = len(data) if dataLen == 0: break totalBytes += dataLen; cFile.write(' ') for i in range(dataLen): cFile.write('0x%02x, ' % ord(data[i])) print >> cFile print >> cFile, '};\n' # Print size and other info cFile.write('const uint32_t g_flashloaderSize = %dU;\n' % totalBytes) cFile.write('const uint32_t g_flashloaderBase = 0x%x;\n' % vectors.st_value) cFile.write('const uint32_t g_flashloaderEntry = 0x%x;\n' % resetHandler.st_value) cFile.write('const uint32_t g_flashloaderStack = 0x%x;\n' % stack.st_value) if __name__ == "__main__": main(sys.argv[1:])
create_fl_image.py腳本里除了普通文件操做外,最關鍵的是這句elfData = elf.ELFObject(),調用了elf.py文件提供的elf格式文件操做接口,經過這些接口獲得了flashloader裏的關鍵信息(vectors、resetHandler、stack),感興趣的能夠本身去分析elf.py文件。
截止目前(2017年),KBOOT支持的Kinetis芯片所有列出在下表:
至此,飛思卡爾Kinetis系列MCU的KBOOT形態痞子衡便介紹完畢了,掌聲在哪裏~~~