KBOOT 是支持配置功能的,配置功能可分為兩方面:一、芯片系統(tǒng)的啟動(dòng)配置;二、KBOOT 特性配置;痞子衡在前一篇文章里介紹了 KBOOT 形態(tài)(ROM/Bootloader/Flashloader),雖然 KBOOT 有三種形態(tài),但實(shí)際上只有 2 種類(lèi)型的芯片載體,即含 ROM 空間的芯片(比如 Kinetis K80)和不含 ROM 空間的芯片(比如 Kinetis KL25),KBOOT 配置在這兩種載體上是有區(qū)別的,下面痞子衡為大家詳解 KBOOT 配置:
一、啟動(dòng)配置:FTFx_FOPT, BOOT Pin, RCM_FM
芯片系統(tǒng)的啟動(dòng)配置主要決定的是芯片上電從哪里(ROM/Flash)開(kāi)始啟動(dòng),所以這個(gè)啟動(dòng)配置對(duì)于含 ROM 空間的芯片特別重要,而不適用于不含 ROM 空間的芯片。
1.1 啟動(dòng)方式選擇 Flash Configuration Field - FOPT
熟悉 Kinetis 芯片的朋友肯定知道,Kinetis 芯片都是含內(nèi)部 Flash 的,內(nèi)部 Flash 起始地址一般是 0x00000000,F(xiàn)lash 操作是通過(guò) FTFx 這個(gè) IP 模塊實(shí)現(xiàn)的,如果你對(duì) FTFx 模塊了解的話(huà),這個(gè) IP 模塊內(nèi)部其實(shí)有一些寄存器屬性是 readonly 的,并且從手冊(cè)里看這些 readonly 寄存器的初值是 undefined,截取 K80 芯片 FTFA 模塊中這些 readonly 寄存器如下:
既然這些寄存器是 readonly 屬性并且初值又是 undefined 的,那么其初值到底取決于什么?這里就涉及到 Kinetis 芯片中比較特別的 FCF 加載機(jī)制,F(xiàn)CF 即 Flash Configuration Field,其區(qū)域地址為 Flash 偏移 0x400 - 0x40f,一共 16 個(gè) bytes,這 16bytes 內(nèi)容組織如下:
任何一次熱啟動(dòng)后,芯片系統(tǒng)會(huì)自動(dòng)從 FCF 區(qū)域加載初值進(jìn) FTFx 相應(yīng)寄存器中,我們主要關(guān)注的是跟啟動(dòng)配置相關(guān)的 FTFx_FOPT 寄存器(特別注意,當(dāng) FCF 中對(duì)應(yīng) FOPT 的值是無(wú)效值 0x00 時(shí),在加載過(guò)程中芯片自動(dòng)會(huì)給 FOPT 賦值 0xFF),下面是 FTFx_FOPT 寄存器的 bit 定義,其中 BOOTSRC_SEL 和 BOOTPIN_OPT 位是關(guān)鍵(注意這兩個(gè)位在不含 ROM 空間的芯片上是 reserved 的)。
BOOTSRC_SEL 和 BOOTPIN_OPT 的值共同決定了芯片的啟動(dòng)位置(ROM/Flash):
BOOTPIN_OPT = 1: 啟動(dòng)位置完全由 BOOTSRC_SEL 決定。
BOOTPIN_OPT = 0: 啟動(dòng)位置由 BOOTSRC_SEL 和 BOOTCFG0 pin 共同決定。
因此當(dāng)在 FCF 里指定 FOPT 為 0xFF 時(shí),芯片上電永遠(yuǎn)從 ROM 啟動(dòng);當(dāng)在 FCF 里指定 FOPT 為 0x3F 時(shí),芯片上電永遠(yuǎn)從 Flash 啟動(dòng)。
1.2 啟動(dòng)位置切換 BOOT Pin
在 1.1 節(jié)的最后痞子衡提到了 BOOTCFG0 pin,其實(shí) BOOTCFG0 pin 對(duì)于含 ROM 空間芯片而言就是 BOOT Pin,這個(gè) BOOT Pin 是芯片系統(tǒng)直接指定的,與 NMI pin 復(fù)用(在上電以及 ROM 執(zhí)行過(guò)程中,NMI pin 原本中斷功能是被屏蔽的)。
你一定會(huì)疑惑 BOOT pin 有什么用?讓我們?cè)倩氐?1.1 節(jié)的最后,0x3F 和 0xFF 是兩種比較典型的 FOPT 啟動(dòng)配置值,但是這種配置值指定的是固定啟動(dòng)位置,除非你擦除 FCF 重新燒寫(xiě),不然無(wú)法輕易改變啟動(dòng)位置。但是有的時(shí)候我們想在不擦除 FCF 情況下自由切換啟動(dòng)位置 ROM/Flash,這時(shí)候就得依靠 BOOT Pin,此時(shí)我們需要在 FCF 里指定 FOPT 為 0x3D,讓我們結(jié)合下面的 TWR-K80F150M 原理圖來(lái)說(shuō)明:
在上述 TWR-K80F150M 原理圖中,我們可以看到兩個(gè)按鍵開(kāi)關(guān)(SW2,SW1)分別連到了 K80 芯片的 NMI_b pin 和 RESET_b pin,當(dāng)我們配置 FOPT 為 0x3D 時(shí),即啟動(dòng)位置由 BOOTSRC_SEL(2'b00,即從 Flash 啟動(dòng))和 BOOTCFG0(NMI)共同決定,如果在 RESET_b pin(SW1)按下復(fù)位過(guò)程中,BOOTCFG0 pin(SW2)一直被按下,那么芯片會(huì)從 ROM 啟動(dòng)(并且超時(shí)也不會(huì)跳轉(zhuǎn)到 Application);而如果 BOOTCFG0 pin(SW2)沒(méi)有被按下,那么芯片會(huì)從 Flash 啟動(dòng)。是不是瞬間覺(jué)得這樣切換啟動(dòng)位置很方便!
其實(shí) BOOT Pin 設(shè)計(jì)不僅僅只在含 ROM 空間的芯片上存在,在不含 ROM 空間的芯片上也支持,只不過(guò)在不含 ROM 空間的芯片上,BOOT Pin 是由 Bootloader 代碼指定的(需要查看芯片手冊(cè) Bootloader 章節(jié)或源代碼),我們知道當(dāng)芯片不含 ROM 時(shí),上電默認(rèn)從 Flash 起始地址處啟動(dòng),而 Flash 起始地址已被 Flash-Resident Bootloader 占據(jù),所以上電永遠(yuǎn)執(zhí)行 Flash-Resident Bootloader,此時(shí) BOOT Pin 的意義主要是決定是否要超時(shí)跳轉(zhuǎn)到 Application,如果 BOOT Pin 在 RESET_b pin 按下復(fù)位過(guò)程中一直被按下,那么芯片將會(huì)一直停留在 Bootloader 中;如果 BOOT Pin 沒(méi)有被按下,那么芯片在執(zhí)行 Bootloader 超時(shí)時(shí)間到了之后會(huì)跳轉(zhuǎn)到 Application。
1.3 強(qiáng)制從 ROM 熱啟動(dòng) RCM_FM
我們知道芯片復(fù)位啟動(dòng)分為冷啟動(dòng)(POR Pin)和熱啟動(dòng)(RESET_b Pin),冷啟動(dòng)是最為徹底的啟動(dòng)(所有寄存器初值全部重置),而熱啟動(dòng)并不是徹底啟動(dòng)(有些寄存器初值不會(huì)重置),RCM 模塊里有 1 個(gè)寄存器(RCM_FM)就只有冷啟動(dòng)才能被重置,而且這個(gè)寄存器與從 ROM 啟動(dòng)息息相關(guān),不得不提。下面是 RCM_FM 和 RCM_MR 寄存器的 bit 定義:
上述兩個(gè)寄存器只在含 ROM 空間的芯片上存在,其作用是為了保證 ROM 在執(zhí)行期間即使不小心發(fā)生熱啟動(dòng),下一次還是會(huì)強(qiáng)制執(zhí)行 ROM 程序,而不受 FOPT, BOOT Pin 狀態(tài)變化影響。ROM 程序里操作 RCM_FM/MR 寄存器使能了這一強(qiáng)制 ROM 啟動(dòng)功能,具體代碼如下:
// ROM statrup 過(guò)程中調(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);
? ? // ...
}
因?yàn)?ROM 里有了上述代碼,所以只要芯片上電執(zhí)行過(guò) ROM 程序,除非是 ROM 主動(dòng)跳轉(zhuǎn)到了 Application 或者發(fā)生了冷啟動(dòng),否則任何與 ROM 有關(guān)的配置修改操作都不會(huì)影響到下一次啟動(dòng) ROM 的執(zhí)行,這種機(jī)制可以確保 Application 一定會(huì)被 ROM 下載進(jìn) Flash。
二、特性配置:BCA
除了啟動(dòng)配置外,KBOOT 還支持特性配置,我們知道 KBOOT 提供的特性功能非常多,比如支持的外設(shè)種類(lèi)豐富、超時(shí)時(shí)間可設(shè)、Application 完整性校驗(yàn)、USB ID 可設(shè)、運(yùn)行時(shí)鐘可配、加密特性支持、QSPI 啟動(dòng)支持,這些特性可以通過(guò) BCA 來(lái)配置,BCA 是 Bootloader Configuration Area 的簡(jiǎn)稱(chēng),KBOOT 通過(guò)從 BCA 區(qū)域加載用戶(hù)配置數(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)體格式準(zhǔn)備好配置數(shù)據(jù),具體數(shù)據(jù)值所代表含義請(qǐng)查看芯片手冊(cè) Bootloader 章節(jié),痞子衡在后續(xù)文章里也會(huì)慢慢講到。此處假設(shè)你已經(jīng)準(zhǔn)備好了 BCA 數(shù)據(jù),那么這個(gè) BCA 數(shù)據(jù)應(yīng)該放在哪里呢?其實(shí) KBOOT 已經(jīng)指定好了 BCA 位置,見(jiàn)如下代碼,BCA 起始地址固定在 APP_VECTOR_TABLE 地址偏移 0x3c0 處,對(duì)于 ROM Bootloader 而言,BCA 地址就是 0x3c0,因?yàn)?APP_VECTOR_TABLE=0;而對(duì)于 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 個(gè)中斷,其中前 16 個(gè)是系統(tǒng)中斷,后 240 個(gè)是外設(shè)中斷,而 Cortex-M 廠(chǎng)商生產(chǎn)的芯片一般用不滿(mǎn) 240 個(gè)外設(shè)中斷,所以其實(shí)中斷向量表后半部分其實(shí)是 reserved 的,因此我們可以把 reserved 區(qū)域里的 0x3C0 - 0x3FF 這 64bytes 用作 BCA 配置。