大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是飛思卡爾Kinetis系列MCU的KBOOT架構。
Bootloader是嵌入式MCU開發裡很常見的一種專用的應用程式,在一個沒有Bootloader的嵌入式系統裡如果要更新Application,只能通過外部硬體偵錯程式/下載器,而如果有了Bootloader,我們可以輕鬆完成Application的更新升級以及載入啟動,除此以外在Bootloader中還可以引入更多高階特性,比如Application完整性檢測、可靠升級、加密特性等。
KBOOT是設計執行於Kinetis晶片上的一種Bootloader,KBOOT由飛思卡爾(現恩智浦)官方推出,其功能非常全面,今天痞子衡就為你揭開KBOOT的神祕面紗:
一、KBOOT由來
飛思卡爾Kinetis系列MCU是從2010年開始推出的,早期的Kinetis產品比如MK60, MKL25並沒有配套標準Bootloader功能,不過可以從飛思卡爾官網上找到很多風格迥異的Bootloader參考設計,比如AN2295(UART型)、AN4655(I2C型)、AN4379(USB-MSD型)等,這些Bootloader參考方案都是不同的飛思卡爾應用工程師設計的,因此所用的通訊協議以及上位機工具都不相同,雖然這些AN一定程度上能解決客戶使用Bootloader的需求,但是在Bootloader後續維護升級以及擴充性方面有一定缺陷。
飛思卡爾也逐漸意識到了這一點,為了完善軟體生態建設與服務質量,於是在2013年初組建了一支專門開發Kinetis Bootloader的軟體團隊,即KBOOT Team,這個Team成立的目的就是要開發出一個Unified Kinetis Bootloader(簡稱KBOOT),這個bootloader必須擁有良好的架構,易於擴充套件和維護,功能全面且經過完善的驗證。
KBOOT專案發展至今(2017)已近5年,目前被廣泛應用於主流Kinetis晶片上,是Kinetis晶片整合Bootloader的首選,其官方主頁是 www.nxp.com/kboot
二、KBOOT架構
從架構角度來分析KBOOT,拋開各種附加特性,其實KBOOT最核心的就是這三大元件:Peripheral Interface、Command & Data Processor、Memory Interface,如下圖所示:
2.1 Peripheral Interface
KBOOT首要功能是能夠與Host進行資料傳輸,我們知道資料傳輸介面種類有很多,KBOOT設計上可同時支援多種常見傳輸介面(UART, SPI, I2C, USB-HID, CAN),為此KBOOT在Peripheral Interface元件中抽象了Peripheral的行為(byte/packet層傳輸等),使得在Peripheral種類擴充上更容易。
KBOOT中使用一個名叫g_peripherals[]的結構體陣列來集合所有外設,下面示例僅包含UART, USB:
//! @brief Peripheral array.
const peripheral_descriptor_t g_peripherals[] = {
#if BL_CONFIG_LPUART_0
// LPUART0
{.typeMask = kPeripheralType_UART,
.instance = 0,
.pinmuxConfig = uart_pinmux_config,
.controlInterface = &g_lpuartControlInterface,
.byteInterface = &g_lpuartByteInterface,
.packetInterface = &g_framingPacketInterface },
#endif // BL_CONFIG_LPUART_0
#if BL_CONFIG_USB_HID
// USB HID
{.typeMask = kPeripheralType_USB_HID,
.instance = 0,
.pinmuxConfig = NULL,
.controlInterface = &g_usbHidControlInterface,
.byteInterface = NULL,
.packetInterface = &g_usbHidPacketInterface },
#endif // BL_CONFIG_USB_HID
{ 0 } // Terminator
};
如下便是用於抽象外設行為的Peripheral descriptor原型,該原型可以描述所有型別的peripheral:
//! @brief Peripheral descriptor.
//!
//! Instances of this struct describe a particular instance of a peripheral that is
//! available for bootloading.
typedef struct PeripheralDescriptor
{
//! @brief Bit mask identifying the peripheral type.
//! See #_peripheral_types for a list of valid bits.
// 外設的型別名,KBOOT用於識別當前外設的型別
uint32_t typeMask;
//! @brief The instance number of the peripheral.
// 外設的編號,KBOOT可以支援同一外設的多個例項
uint32_t instance;
//! @brief Configure pinmux setting for the peripheral.
// 外設的I/O初始化
void (*pinmuxConfig)(uint32_t instance, pinmux_type_t pinmux);
//! @brief Control interface for the peripheral.
// 外設的行為控制
const peripheral_control_interface_t *controlInterface;
//! @brief Byte-level interface for the peripheral.
//! May be NULL since not all periperhals support this interface.
// 外設的byte級別傳輸控制
const peripheral_byte_inteface_t *byteInterface;
//! @brief Packet level interface for the peripheral.
// 外設的packet級別傳輸控制
const peripheral_packet_interface_t *packetInterface;
} peripheral_descriptor_t;
//! @brief Peripheral control interface.
typedef struct _peripheral_control_interface
{
// 檢測是否外設是否被啟用
bool (*pollForActivity)(const peripheral_descriptor_t *self);
// 外設IP底層初始化
status_t (*init)(const peripheral_descriptor_t *self, serial_byte_receive_func_t function);
// 外設IP底層恢復
void (*shutdown)(const peripheral_descriptor_t *self);
// 特殊外設pump控制(比如USB-MSC, DFU等)
void (*pump)(const peripheral_descriptor_t *self);
} peripheral_control_interface_t;
//! @brief Peripheral abstract byte interface.
typedef struct _peripheral_byte_inteface
{
// byte傳輸初始化,一般為NULL
status_t (*init)(const peripheral_descriptor_t *self);
// byte傳送
status_t (*write)(const peripheral_descriptor_t *self, const uint8_t *buffer, uint32_t byteCount);
} peripheral_byte_inteface_t;
//! @brief Peripheral Packet Interface.
typedef struct _peripheral_packet_interface
{
// packet傳輸初始化
status_t (*init)(const peripheral_descriptor_t *self);
// 接收一包packet
status_t (*readPacket)(const peripheral_descriptor_t *self, uint8_t **packet, uint32_t *packetLength, packet_type_t packetType);
// 傳送一包packet
status_t (*writePacket)(const peripheral_descriptor_t *self, const uint8_t *packet, uint32_t byteCount, packet_type_t packetType);
// 立即終止當前packet
void (*abortDataPhase)(const peripheral_descriptor_t *self);
// 完成當前packet
status_t (*finalize)(const peripheral_descriptor_t *self);
// 獲取最大packet包長
uint32_t (*getMaxPacketSize)(const peripheral_descriptor_t *self);
// byte接收callback
void (*byteReceivedCallback)(uint8_t byte);
} peripheral_packet_interface_t;
2.2 Memory Interface
KBOOT其次功能是能夠讀寫儲存空間,Kinetis上涉及的儲存空間包括內部SRAM, Flash,Register、I/O以及外部QuadSPI NOR Flash(可以對映在MCU內部儲存空間),為此KBOOT在Memory Interface元件中抽象了Memory的行為(read/write/erase等),使得在Memory種類擴充上更容易。
KBOOT中使用一個名叫g_memoryMap[]的結構體陣列來集合所有儲存空間,下面示例包含了典型的儲存空間(Flash、RAM、Register、I/O、QSPI NOR Flash):
//! @brief Memory map.
//!
//! This map is not const because it is updated at runtime with the actual sizes of
//! flash and RAM for the chip we're running on.
//! @note Do not change the index of Flash, SRAM, or QSPI (see memory.h).
memory_map_entry_t g_memoryMap[] = {
{ 0x00000000, 0x0003ffff, kMemoryIsExecutable, &g_flashMemoryInterface }, // Flash array (256KB)
{ 0x1fff0000, 0x2002ffff, kMemoryIsExecutable, &g_normalMemoryInterface }, // SRAM (256KB)
{ 0x68000000, 0x6fffffff, kMemoryNotExecutable, &g_qspiMemoryInterface }, // QSPI memory
{ 0x04000000, 0x07ffffff, kMemoryNotExecutable, &g_qspiAliasAreaInterface }, // QSPI alias area
{ 0x40000000, 0x4007ffff, kMemoryNotExecutable, &g_deviceMemoryInterface }, // AIPS0 peripherals
{ 0x40080000, 0x400fefff, kMemoryNotExecutable, &g_deviceMemoryInterface }, // AIPS1 peripherals
{ 0x400ff000, 0x400fffff, kMemoryNotExecutable, &g_deviceMemoryInterface }, // GPIO
{ 0xe0000000, 0xe00fffff, kMemoryNotExecutable, &g_deviceMemoryInterface }, // M4 private peripherals
{ 0 } // Terminator
};
如下便是用於抽象儲存器操作的memory_map_entry原型,該原型可以描述所有型別的memory:
//! @brief Structure of a memory map entry.
typedef struct _memory_map_entry
{
// 儲存空間起始地址
uint32_t startAddress;
// 儲存空間結束地址
uint32_t endAddress;
// 儲存空間屬性(Flash/RAM,是否能XIP)
uint32_t memoryProperty;
// 儲存空間操作介面
const memory_region_interface_t *memoryInterface;
} memory_map_entry_t;
typedef struct _memory_region_interface
{
// 儲存空間(IP控制器)初始化
status_t (*init)(void);
// 從儲存空間指定範圍內讀取資料
status_t (*read)(uint32_t address, uint32_t length, uint8_t *buffer);
// 將資料寫入儲存空間指定範圍內
status_t (*write)(uint32_t address, uint32_t length, const uint8_t *buffer);
// 將pattern填充入儲存空間指定範圍內
status_t (*fill)(uint32_t address, uint32_t length, uint32_t pattern);
// 對於支援page/section程式設計的儲存器做一次page/section資料寫入
status_t (*flush)(void);
// 將儲存空間指定範圍內容擦除
status_t (*erase)(uint32_t address, uint32_t length);
} memory_region_interface_t;
2.3 Command & Data Processor
KBOOT核心功能便是與Host之間的命令互動,KBOOT主要工作於Slave模式,實時監聽來自Host的命令並做出響應,KBOOT僅能識別事先規定好的命令格式,因此KBOOT必須配套一個專用上位機工具使用。你可能會疑問,為什麼這個元件又叫Data Processor?因為有些命令是含有Data phase的(比如read memory, write memory),對於這些命令時除了基本的命令互動響應之後,還必須有資料傳輸互動響應。
KBOOT中使用如下名叫g_commandInterface和g_commandHandlerTable[]的結構變數來實現核心命令互動,KBOOT中一共實現了19條命令:
// See bl_command.h for documentation on this interface.
command_interface_t g_commandInterface =
{
bootloader_command_init,
bootloader_command_pump,
(command_handler_entry_t *)&g_commandHandlerTable,
&g_commandData
};
//! @brief Command handler table.
const command_handler_entry_t g_commandHandlerTable[] = {
// cmd handler // data handler or NULL
{ handle_flash_erase_all, NULL }, // kCommandTag_FlashEraseAll = 0x01
{ handle_flash_erase_region, NULL }, // kCommandTag_FlashEraseRegion = 0x02
{ handle_read_memory, handle_data_producer }, // kCommandTag_ReadMemory = 0x03
{ handle_write_memory, handle_data_consumer }, // kCommandTag_WriteMemory = 0x04
{ handle_fill_memory, NULL }, // kCommandTag_FillMemory = 0x05
{ handle_flash_security_disable, NULL }, // kCommandTag_FlashSecurityDisable = 0x06
{ handle_get_property, NULL }, // kCommandTag_GetProperty = 0x07
{ handle_receive_sb_file, handle_data_consumer }, // kCommandTag_ReceiveSbFile = 0x08
{ handle_execute, NULL }, // kCommandTag_Execute = 0x09
{ handle_call, NULL }, // kCommandTag_Call = 0x0a
{ handle_reset, NULL }, // kCommandTag_Reset = 0x0b
{ handle_set_property, NULL }, // kCommandTag_SetProperty = 0x0c
{ handle_flash_erase_all_unsecure, NULL }, // kCommandTag_FlashEraseAllUnsecure = 0x0d
{ handle_flash_program_once, NULL }, // kCommandTag_ProgramOnce = 0x0e
{ handle_flash_read_once, NULL }, // kCommandTag_ReadOnce = 0x0f
{ handle_flash_read_resource, handle_data_producer }, // kCommandTag_ReadResource = 0x10
{ handle_configure_memory, NULL }, // kCommandTag_ConfigureMemory = 0x11
{ handle_reliable_update, NULL }, // kCommandTag_ReliableUpdate = 0x12
{ handle_generate_key_blob, handle_key_blob_data }, // kCommandTag_GenerateKeyBlob = 0x13
};
如下便是用於核心命令互動的Command interface原型:
//! @brief Interface to command processor operations.
typedef struct CommandInterface
{
// command處理控制單元初始化
status_t (*init)(void);
// command處理控制單元pump
status_t (*pump)(void);
// command服務函式查詢表
const command_handler_entry_t *handlerTable;
// command處理控制單元狀態資料
command_processor_data_t *stateData;
} command_interface_t;
//! @brief Format of command handler entry.
typedef struct CommandHandlerEntry
{
// command服務函式
void (*handleCommand)(uint8_t *packet, uint32_t packetLength);
// command的data級處理函式(只有少部分command有此函式)
status_t (*handleData)(bool *hasMoreData);
} command_handler_entry_t;
//! @brief Command processor data format.
typedef struct CommandProcessorData
{
// command處理控制狀態機當前狀態(command/data兩種狀態)
int32_t state;
// 指向當前處理的packet地址
uint8_t *packet;
// 當前處理的packet長度
uint32_t packetLength;
// command的data級處理控制狀態資料
struct DataPhase
{
uint8_t *data; //!< Data for data phase
uint32_t count; //!< Remaining count to produce/consume
uint32_t address; //!< Address for data phase
uint32_t memoryId; //!< ID of the target memory
uint32_t dataBytesAvailable; //!< Number of bytes available at data pointer
uint8_t commandTag; //!< Tag of command running data phase
uint8_t option; //!< option for special command
} dataPhase;
// 指向command服務函式查詢表地址
const command_handler_entry_t *handlerEntry; //! Pointer to handler table entry for packet in process
} command_processor_data_t;
至此,飛思卡爾Kinetis系列MCU的KBOOT架構痞子衡便介紹完畢了,掌聲在哪裡~~~