你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們介紹的是飛思卡爾Kinetis系列MCU的KBOOT架構。編程
Bootloader是嵌入式MCU開發裏很常見的一種專用的應用程序,在一個沒有Bootloader的嵌入式系統裏若是要更新Application,只能經過外部硬件調試器/下載器,而若是有了Bootloader,咱們能夠輕鬆完成Application的更新升級以及加載啓動,除此之外在Bootloader中還能夠引入更多高級特性,好比Application完整性檢測、可靠升級、加密特性等。
KBOOT是設計運行於Kinetis芯片上的一種Bootloader,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最核心的就是這三大組件:Peripheral Interface、Command & Data Processor、Memory Interface,以下圖所示:ide
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;
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):ui
//! @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:this
//! @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;
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架構痞子衡便介紹完畢了,掌聲在哪裏~~~