天天看點

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

我們知道KBOOT是一個完善的Bootloader解決方案,這個解決方案主要設計用于Kinetis晶片上,目前Kinetis晶片起碼有上百種型号,KBOOT在這上百種Kinetis晶片裡存在的形式并不是完全一樣的,KBOOT主要有三種存在形式(ROM Bootloader、Flashloader、Flash-Resident Bootloader)

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是飛思卡爾Kinetis系列MCU的KBOOT形态。

  痞子衡在前一篇文章裡簡介了 KBOOT架構,我們知道KBOOT是一個完善的Bootloader解決方案,這個解決方案主要設計用于Kinetis晶片上,目前Kinetis晶片起碼有上百種型号,KBOOT在這上百種Kinetis晶片裡存在的形式并不是完全一樣的,KBOOT主要有三種存在形式(ROM Bootloader、Flashloader、Flash-Resident Bootloader),下面痞子衡為大家細說這三種形态:

一、KBOOT形态差別

  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)區域。

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

  下表是KBOOT三種形态的對比,分别從use case、delivery mechanism、supported device、clock configuration、feature五大角度進行了對比:

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

  總結來說,可以這麼看KBOOT這三種存在的由來:

  • 對于2014年初及以後問世的Kinetis晶片(比如MKL03、MKL27、MKL43、MKL80、MKE18F等),晶片内基本都是含ROM空間的,是以KBOOT是以ROM Bootloader的形式存在的;
  • 對于2014年初及以後主推的Kinetis晶片(比如MK22、MK65、MKV31、MKS22等),晶片内雖然沒有ROM空間,但飛思卡爾希望能給客戶提供至少一次免程式設計器燒錄Application(用于量産)的機會,是以KBOOT是以Flashloader的形式存在的;
  • 對于在市場上主流又暢銷的Kinetis晶片(比如MKL25、MK22、MK66、MKL28等),不管晶片内是否有ROM空間,飛思卡爾都希望能夠給出Bootloader源碼,以便讓客戶自由修改來滿足其個性化需求,是以KBOOT是以Flash-Resident Bootloader的形式存在的;

二、KBOOT各形态實作

2.1 ROM 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。

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

2.2 Flash-Resident Bootloader

  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指定)并跳轉過去執行。

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

2.3 Flashloader

  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隻能被使用一次的原因。

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)
2.3.1 loader機制

  關于loader機制與實作,有必要詳細講解一下,讓我們結合代碼分析,首先從官網下載下傳NXP_Kinetis_Bootloader_2_0_0.zip包,就以KS22晶片為例(\targets\MKS22F25612\bootloader.eww):

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

  使用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裡執行的關鍵所在。

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

  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。

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

  在\bin目錄下存放了所有腳本檔案,當然也包括create_flashloader_image.bat,先打開這個腳本看一下:

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)
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檔案。

三、KBOOT各形态晶片支援

  截止目前(2017年),KBOOT支援的Kinetis晶片全部列出在下表:

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

  至此,飛思卡爾Kinetis系列MCU的KBOOT形态痞子衡便介紹完畢了,掌聲在哪裡~~~

歡迎訂閱

文章會同時釋出到我的 部落格園首頁、CSDN首頁、微信公衆号 平台上。

微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

  最後歡迎關注痞子衡個人微信公衆号【痞子衡嵌入式】,一個專注嵌入式技術的公衆号,跟着痞子衡一起玩轉嵌入式。

痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)
痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)
痞子衡嵌入式:飛思卡爾Kinetis系列MCU啟動那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)

  衡傑(痞子衡),目前就職于恩智浦MCU系統部門,擔任嵌入式系統應用工程師。

  專欄内所有文章的轉載請注明出處:http://www.cnblogs.com/henjay724/

  與痞子衡進一步交流或咨詢業務合作請發郵件至 [email protected]

  可以關注痞子衡的Github首頁 https://github.com/JayHeng,有很多好玩的嵌入式項目。

  關于專欄文章有任何疑問請直接在部落格下面留言,痞子衡會及時回複免費(劃重點)答疑。

  痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。