KBOOT 是支持配置功能的,配置功能可分為兩方面:一、芯片系統(tǒng)的啟動配置;二、KBOOT 特性配置;痞子衡在前一篇文章里介紹了 KBOOT 形態(tài)(ROM/Bootloader/Flashloader),雖然 KBOOT 有三種形態(tài),但實際上只有 2 種類型的芯片載體,即含 ROM 空間的芯片(比如 Kinetis K80)和不含 ROM 空間的芯片(比如 Kinetis KL25),KBOOT 配置在這兩種載體上是有區(qū)別的,下面痞子衡為大家詳解 KBOOT 配置:
一、啟動配置:FTFx_FOPT, BOOT Pin, RCM_FM
芯片系統(tǒng)的啟動配置主要決定的是芯片上電從哪里(ROM/Flash)開始啟動,所以這個啟動配置對于含 ROM 空間的芯片特別重要,而不適用于不含 ROM 空間的芯片。
1.1 啟動方式選擇 Flash Configuration Field - FOPT
熟悉 Kinetis 芯片的朋友肯定知道,Kinetis 芯片都是含內(nèi)部 Flash 的,內(nèi)部 Flash 起始地址一般是 0x00000000,F(xiàn)lash 操作是通過 FTFx 這個 IP 模塊實現(xiàn)的,如果你對 FTFx 模塊了解的話,這個 IP 模塊內(nèi)部其實有一些寄存器屬性是 readonly 的,并且從手冊里看這些 readonly 寄存器的初值是 undefined,截取 K80 芯片 FTFA 模塊中這些 readonly 寄存器如下:
既然這些寄存器是 readonly 屬性并且初值又是 undefined 的,那么其初值到底取決于什么?這里就涉及到 Kinetis 芯片中比較特別的 FCF 加載機制,F(xiàn)CF 即 Flash Configuration Field,其區(qū)域地址為 Flash 偏移 0x400 - 0x40f,一共 16 個 bytes,這 16bytes 內(nèi)容組織如下:
任何一次熱啟動后,芯片系統(tǒng)會自動從 FCF 區(qū)域加載初值進 FTFx 相應(yīng)寄存器中,我們主要關(guān)注的是跟啟動配置相關(guān)的 FTFx_FOPT 寄存器(特別注意,當 FCF 中對應(yīng) FOPT 的值是無效值 0x00 時,在加載過程中芯片自動會給 FOPT 賦值 0xFF),下面是 FTFx_FOPT 寄存器的 bit 定義,其中 BOOTSRC_SEL 和 BOOTPIN_OPT 位是關(guān)鍵(注意這兩個位在不含 ROM 空間的芯片上是 reserved 的)。
BOOTSRC_SEL 和 BOOTPIN_OPT 的值共同決定了芯片的啟動位置(ROM/Flash):
BOOTPIN_OPT = 1: 啟動位置完全由 BOOTSRC_SEL 決定。
BOOTPIN_OPT = 0: 啟動位置由 BOOTSRC_SEL 和 BOOTCFG0 pin 共同決定。
因此當在 FCF 里指定 FOPT 為 0xFF 時,芯片上電永遠從 ROM 啟動;當在 FCF 里指定 FOPT 為 0x3F 時,芯片上電永遠從 Flash 啟動。
1.2 啟動位置切換 BOOT Pin
在 1.1 節(jié)的最后痞子衡提到了 BOOTCFG0 pin,其實 BOOTCFG0 pin 對于含 ROM 空間芯片而言就是 BOOT Pin,這個 BOOT Pin 是芯片系統(tǒng)直接指定的,與 NMI pin 復用(在上電以及 ROM 執(zhí)行過程中,NMI pin 原本中斷功能是被屏蔽的)。
你一定會疑惑 BOOT pin 有什么用?讓我們再回到 1.1 節(jié)的最后,0x3F 和 0xFF 是兩種比較典型的 FOPT 啟動配置值,但是這種配置值指定的是固定啟動位置,除非你擦除 FCF 重新燒寫,不然無法輕易改變啟動位置。但是有的時候我們想在不擦除 FCF 情況下自由切換啟動位置 ROM/Flash,這時候就得依靠 BOOT Pin,此時我們需要在 FCF 里指定 FOPT 為 0x3D,讓我們結(jié)合下面的 TWR-K80F150M 原理圖來說明:
在上述 TWR-K80F150M 原理圖中,我們可以看到兩個按鍵開關(guān)(SW2,SW1)分別連到了 K80 芯片的 NMI_b pin 和 RESET_b pin,當我們配置 FOPT 為 0x3D 時,即啟動位置由 BOOTSRC_SEL(2'b00,即從 Flash 啟動)和 BOOTCFG0(NMI)共同決定,如果在 RESET_b pin(SW1)按下復位過程中,BOOTCFG0 pin(SW2)一直被按下,那么芯片會從 ROM 啟動(并且超時也不會跳轉(zhuǎn)到 Application);而如果 BOOTCFG0 pin(SW2)沒有被按下,那么芯片會從 Flash 啟動。是不是瞬間覺得這樣切換啟動位置很方便!
其實 BOOT Pin 設(shè)計不僅僅只在含 ROM 空間的芯片上存在,在不含 ROM 空間的芯片上也支持,只不過在不含 ROM 空間的芯片上,BOOT Pin 是由 Bootloader 代碼指定的(需要查看芯片手冊 Bootloader 章節(jié)或源代碼),我們知道當芯片不含 ROM 時,上電默認從 Flash 起始地址處啟動,而 Flash 起始地址已被 Flash-Resident Bootloader 占據(jù),所以上電永遠執(zhí)行 Flash-Resident Bootloader,此時 BOOT Pin 的意義主要是決定是否要超時跳轉(zhuǎn)到 Application,如果 BOOT Pin 在 RESET_b pin 按下復位過程中一直被按下,那么芯片將會一直停留在 Bootloader 中;如果 BOOT Pin 沒有被按下,那么芯片在執(zhí)行 Bootloader 超時時間到了之后會跳轉(zhuǎn)到 Application。
1.3 強制從 ROM 熱啟動 RCM_FM
我們知道芯片復位啟動分為冷啟動(POR Pin)和熱啟動(RESET_b Pin),冷啟動是最為徹底的啟動(所有寄存器初值全部重置),而熱啟動并不是徹底啟動(有些寄存器初值不會重置),RCM 模塊里有 1 個寄存器(RCM_FM)就只有冷啟動才能被重置,而且這個寄存器與從 ROM 啟動息息相關(guān),不得不提。下面是 RCM_FM 和 RCM_MR 寄存器的 bit 定義:
上述兩個寄存器只在含 ROM 空間的芯片上存在,其作用是為了保證 ROM 在執(zhí)行期間即使不小心發(fā)生熱啟動,下一次還是會強制執(zhí)行 ROM 程序,而不受 FOPT, BOOT Pin 狀態(tài)變化影響。ROM 程序里操作 RCM_FM/MR 寄存器使能了這一強制 ROM 啟動功能,具體代碼如下:
// ROM statrup 過程中調(diào)用的函數(shù)
void SystemInit (void)
{
? ? // ...
? ? // Set Force ROM bits in RCM. We only set bit 2, so the RCM_MR register doesn't
? ? // falsely show that the ROM was booted via boot pin assertion.
? ? RCM->FM = RCM_FM_FORCEROM(2);
? ? // ...
}
// ROM 跳轉(zhuǎn)到 Application 之前調(diào)用的函數(shù)
void shutdown_cleanup(bool isShutdown)
{
? ? // ...
? ? // Disable force ROM.
? ? RCM->FM = RCM_FM_FORCEROM(0);
? ? // Clear status register (bits are w1c).
? ? RCM->MR = RCM_MR_BOOTROM(3);
? ? // ...
}
因為 ROM 里有了上述代碼,所以只要芯片上電執(zhí)行過 ROM 程序,除非是 ROM 主動跳轉(zhuǎn)到了 Application 或者發(fā)生了冷啟動,否則任何與 ROM 有關(guān)的配置修改操作都不會影響到下一次啟動 ROM 的執(zhí)行,這種機制可以確保 Application 一定會被 ROM 下載進 Flash。
二、特性配置:BCA
除了啟動配置外,KBOOT 還支持特性配置,我們知道 KBOOT 提供的特性功能非常多,比如支持的外設(shè)種類豐富、超時時間可設(shè)、Application 完整性校驗、USB ID 可設(shè)、運行時鐘可配、加密特性支持、QSPI 啟動支持,這些特性可以通過 BCA 來配置,BCA 是 Bootloader Configuration Area 的簡稱,KBOOT 通過從 BCA 區(qū)域加載用戶配置數(shù)據(jù)完成這些特性配置。BCA 配置結(jié)構(gòu)體原型如下(以 K80 芯片為例):
//! @brief Format of bootloader configuration data on Flash.
typedef struct BootloaderConfigurationData
{
? ? uint32_t tag; ? ? ? ? ? ? ? ? ? ? ? ? ?//!< [00:03] Tag value used to validate the BCA data. Must be set to 'kcfg'.
? ? uint32_t crcStartAddress; ? ? ? ? ? ? ?//!< [04:07]
? ? uint32_t crcByteCount; ? ? ? ? ? ? ? ? //!< [08:0b]
? ? uint32_t crcExpectedValue; ? ? ? ? ? ? //!< [0c:0f]
? ? uint8_t enabledPeripherals; ? ? ? ? ? ?//!< [10:10]
? ? uint8_t i2cSlaveAddress; ? ? ? ? ? ? ? //!< [11:11]
? ? uint16_t peripheralDetectionTimeoutMs; //!< [12:13] Timeout in milliseconds for peripheral detection before jumping to application code
? ? uint16_t usbVid; ? ? ? ? ? ? ? ? ? ? ? //!< [14:15]
? ? uint16_t usbPid; ? ? ? ? ? ? ? ? ? ? ? //!< [16:17]
? ? uint32_t usbStringsPointer; ? ? ? ? ? ?//!< [18:1b]
? ? uint8_t clockFlags; ? ? ? ? ? ? ? ? ? ?//!< [1c:1c] High Speed and other clock options
? ? uint8_t clockDivider; ? ? ? ? ? ? ? ? ?//!< [1d:1d] One's complement of clock divider, zero divider is divide by 1
? ? uint8_t bootFlags; ? ? ? ? ? ? ? ? ? ? //!< [1e:1e] One's complemnt of direct boot flag, 0xFE represents direct boot
? ? uint8_t pad0; ? ? ? ? ? ? ? ? ? ? ? ? ?//!< [1f:1f] One's complemnt of direct boot flag, 0xFE represents direct boot
? ? uint32_t mmcauConfigPointer; ? ? ? ? ? //!< [20:23] Holds a pointer value to the MMCAU configuration
? ? uint32_t keyBlobPointer; ? ? ? ? ? ? ? //!< [24:27] Holds a pointer value to the key blob array used to configure OTFAD
? ? uint8_t reserved[8]; ? ? ? ? ? ? ? ? ? //!< [28:2f] Reserved.
? ? uint32_t qspi_config_block_pointer; ? ?//!< [30:33] QSPI config block pointer.
} bootloader_configuration_data_t;
如果你想配置 KBOOT 的特性,必須按上述結(jié)構(gòu)體格式準備好配置數(shù)據(jù),具體數(shù)據(jù)值所代表含義請查看芯片手冊 Bootloader 章節(jié),痞子衡在后續(xù)文章里也會慢慢講到。此處假設(shè)你已經(jīng)準備好了 BCA 數(shù)據(jù),那么這個 BCA 數(shù)據(jù)應(yīng)該放在哪里呢?其實 KBOOT 已經(jīng)指定好了 BCA 位置,見如下代碼,BCA 起始地址固定在 APP_VECTOR_TABLE 地址偏移 0x3c0 處,對于 ROM Bootloader 而言,BCA 地址就是 0x3c0,因為 APP_VECTOR_TABLE=0;而對于 Flash-Resident Bootloader 而言,BCA 地址是 Bootloader 指定的 Application 起始地址偏移 0x3c0 處。
//! @brief Flash constants.
enum _flash_constants
{
? ? //! @brief The bootloader configuration data location .
? ? //!
? ? //! A User Application should populate a BootloaderConfigurationData
? ? //! struct at 0x3c0 from the beginning of the application image which must
? ? //! be the User Application vector table for the flash-resident bootloader
? ? //! collaboration.
? ? kBootloaderConfigAreaAddress = (uint32_t)(APP_VECTOR_TABLE) + 0x3c0
};
最后再解釋一下 BCA 地址為何是 APP_VECTOR_TABLE + 0x3c0,我們知道 ARM Cortex-M 系統(tǒng)規(guī)定 Application 前 1KB(0x0 - 0x3FF)應(yīng)放中斷向量表,Cortex-M 最大支持 256 個中斷,其中前 16 個是系統(tǒng)中斷,后 240 個是外設(shè)中斷,而 Cortex-M 廠商生產(chǎn)的芯片一般用不滿 240 個外設(shè)中斷,所以其實中斷向量表后半部分其實是 reserved 的,因此我們可以把 reserved 區(qū)域里的 0x3C0 - 0x3FF 這 64bytes 用作 BCA 配置。