查看: 1478|回复: 0

一种通用MCU Bootloader架构 - KBOOT

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3298

    主题

    6545

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32003
    最后登录
    2024-4-9
    发表于 2020-1-7 12:52:08 | 显示全部楼层 |阅读模式
     一种通用MCU Bootloader架构 - 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:
    1. //! @brief Peripheral array.
    2. const peripheral_descriptor_t g_peripherals[] = {
    3. #if BL_CONFIG_LPUART_0
    4.     // LPUART0
    5.     {.typeMask = kPeripheralType_UART,
    6.      .instance = 0,
    7.      .pinmuxConfig = uart_pinmux_config,
    8.      .controlInterface = &g_lpuartControlInterface,
    9.      .byteInterface = &g_lpuartByteInterface,
    10.      .packetInterface = &g_framingPacketInterface },
    11. #endif // BL_CONFIG_LPUART_0

    12. #if BL_CONFIG_USB_HID
    13.     // USB HID
    14.     {.typeMask = kPeripheralType_USB_HID,
    15.      .instance = 0,
    16.      .pinmuxConfig = NULL,
    17.      .controlInterface = &g_usbHidControlInterface,
    18.      .byteInterface = NULL,
    19.      .packetInterface = &g_usbHidPacketInterface },
    20. #endif    // BL_CONFIG_USB_HID

    21.     { 0 } // Terminator
    22. };
    复制代码
    如下便是用于抽象外设行为的Peripheral descriptor原型,该原型可以描述所有类型的peripheral:
    1. //! @brief Peripheral descriptor.
    2. //!
    3. //! Instances of this struct describe a particular instance of a peripheral that is
    4. //! available for bootloading.
    5. typedef struct PeripheralDescriptor
    6. {
    7.     //! @brief Bit mask identifying the peripheral type.
    8.     //! See #_peripheral_types for a list of valid bits.
    9.     // 外设的类型名,KBOOT用于识别当前外设的类型
    10.     uint32_t typeMask;

    11.     //! @brief The instance number of the peripheral.
    12.     // 外设的编号,KBOOT可以支持同一外设的多个实例
    13.     uint32_t instance;

    14.     //! @brief Configure pinmux setting for the peripheral.
    15.     // 外设的I/O初始化
    16.     void (*pinmuxConfig)(uint32_t instance, pinmux_type_t pinmux);

    17.     //! @brief Control interface for the peripheral.
    18.     // 外设的行为控制
    19.     const peripheral_control_interface_t *controlInterface;

    20.     //! @brief Byte-level interface for the peripheral.
    21.     //! May be NULL since not all periperhals support this interface.
    22.     // 外设的byte级别传输控制
    23.     const peripheral_byte_inteface_t *byteInterface;

    24.     //! @brief Packet level interface for the peripheral.
    25.     // 外设的packet级别传输控制
    26.     const peripheral_packet_interface_t *packetInterface;
    27. } peripheral_descriptor_t;

    28. //! @brief Peripheral control interface.
    29. typedef struct _peripheral_control_interface
    30. {
    31.     // 检测是否外设是否被激活
    32.     bool (*pollForActivity)(const peripheral_descriptor_t *self);
    33.     // 外设IP底层初始化
    34.     status_t (*init)(const peripheral_descriptor_t *self, serial_byte_receive_func_t function);
    35.     // 外设IP底层恢复
    36.     void (*shutdown)(const peripheral_descriptor_t *self);
    37.     // 特殊外设pump控制(比如USB-MSC, DFU等)
    38.     void (*pump)(const peripheral_descriptor_t *self);
    39. } peripheral_control_interface_t;

    40. //! @brief Peripheral abstract byte interface.
    41. typedef struct _peripheral_byte_inteface
    42. {
    43.     // byte传输初始化,一般为NULL
    44.     status_t (*init)(const peripheral_descriptor_t *self);
    45.     // byte发送
    46.     status_t (*write)(const peripheral_descriptor_t *self, const uint8_t *buffer, uint32_t byteCount);
    47. } peripheral_byte_inteface_t;

    48. //! @brief Peripheral Packet Interface.
    49. typedef struct _peripheral_packet_interface
    50. {
    51.     // packet传输初始化
    52.     status_t (*init)(const peripheral_descriptor_t *self);
    53.     // 接收一包packet
    54.     status_t (*readPacket)(const peripheral_descriptor_t *self, uint8_t **packet, uint32_t *packetLength, packet_type_t packetType);
    55.     // 发送一包packet
    56.     status_t (*writePacket)(const peripheral_descriptor_t *self, const uint8_t *packet, uint32_t byteCount, packet_type_t packetType);
    57.     // 立即终止当前packet
    58.     void (*abortDataPhase)(const peripheral_descriptor_t *self);
    59.     // 完成当前packet
    60.     status_t (*finalize)(const peripheral_descriptor_t *self);
    61.     // 获取最大packet包长
    62.     uint32_t (*getMaxPacketSize)(const peripheral_descriptor_t *self);
    63.     // byte接收callback
    64.     void (*byteReceivedCallback)(uint8_t byte);
    65. } 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):
    1. //! @brief Memory map.
    2. //!
    3. //! This map is not const because it is updated at runtime with the actual sizes of
    4. //! flash and RAM for the chip we're running on.
    5. //! @note Do not change the index of Flash, SRAM, or QSPI (see memory.h).
    6. memory_map_entry_t g_memoryMap[] = {
    7.     { 0x00000000, 0x0003ffff, kMemoryIsExecutable, &g_flashMemoryInterface },    // Flash array (256KB)
    8.     { 0x1fff0000, 0x2002ffff, kMemoryIsExecutable, &g_normalMemoryInterface },   // SRAM (256KB)
    9.     { 0x68000000, 0x6fffffff, kMemoryNotExecutable, &g_qspiMemoryInterface },    // QSPI memory
    10.     { 0x04000000, 0x07ffffff, kMemoryNotExecutable, &g_qspiAliasAreaInterface }, // QSPI alias area
    11.     { 0x40000000, 0x4007ffff, kMemoryNotExecutable, &g_deviceMemoryInterface },  // AIPS0 peripherals
    12.     { 0x40080000, 0x400fefff, kMemoryNotExecutable, &g_deviceMemoryInterface },  // AIPS1 peripherals
    13.     { 0x400ff000, 0x400fffff, kMemoryNotExecutable, &g_deviceMemoryInterface },  // GPIO
    14.     { 0xe0000000, 0xe00fffff, kMemoryNotExecutable, &g_deviceMemoryInterface },  // M4 private peripherals
    15.     { 0 }                                                                        // Terminator
    16. };
    复制代码
    如下便是用于抽象存储器操作的memory_map_entry原型,该原型可以描述所有类型的memory:
    1. //! @brief Structure of a memory map entry.
    2. typedef struct _memory_map_entry
    3. {
    4.     // 存储空间起始地址
    5.     uint32_t startAddress;
    6.     // 存储空间结束地址
    7.     uint32_t endAddress;
    8.     // 存储空间属性(Flash/RAM,是否能XIP)
    9.     uint32_t memoryProperty;
    10.     // 存储空间操作接口
    11.     const memory_region_interface_t *memoryInterface;
    12. } memory_map_entry_t;

    13. typedef struct _memory_region_interface
    14. {
    15.     // 存储空间(IP控制器)初始化
    16.     status_t (*init)(void);
    17.     // 从存储空间指定范围内读取数据
    18.     status_t (*read)(uint32_t address, uint32_t length, uint8_t *buffer);
    19.     // 将数据写入存储空间指定范围内
    20.     status_t (*write)(uint32_t address, uint32_t length, const uint8_t *buffer);
    21.     // 将pattern填充入存储空间指定范围内
    22.     status_t (*fill)(uint32_t address, uint32_t length, uint32_t pattern);
    23.     // 对于支持page/section编程的存储器做一次page/section数据写入
    24.     status_t (*flush)(void);
    25.     // 将存储空间指定范围内容擦除
    26.     status_t (*erase)(uint32_t address, uint32_t length);
    27. } 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条命令:

    1. // See bl_command.h for documentation on this interface.
    2. command_interface_t g_commandInterface =
    3. {
    4.     bootloader_command_init,
    5.     bootloader_command_pump,
    6.     (command_handler_entry_t *)&g_commandHandlerTable,
    7.     &g_commandData
    8. };

    9. //! @brief Command handler table.
    10. const command_handler_entry_t g_commandHandlerTable[] = {
    11.     // cmd handler              // data handler or NULL
    12.     { handle_flash_erase_all, NULL },              // kCommandTag_FlashEraseAll = 0x01
    13.     { handle_flash_erase_region, NULL },           // kCommandTag_FlashEraseRegion = 0x02
    14.     { handle_read_memory, handle_data_producer },  // kCommandTag_ReadMemory = 0x03
    15.     { handle_write_memory, handle_data_consumer }, // kCommandTag_WriteMemory = 0x04
    16.     { handle_fill_memory, NULL },                  // kCommandTag_FillMemory = 0x05
    17.     { handle_flash_security_disable, NULL },       // kCommandTag_FlashSecurityDisable = 0x06
    18.     { handle_get_property, NULL },                    // kCommandTag_GetProperty = 0x07
    19.     { handle_receive_sb_file, handle_data_consumer }, // kCommandTag_ReceiveSbFile = 0x08
    20.     { handle_execute, NULL },                         // kCommandTag_Execute = 0x09
    21.     { handle_call, NULL },                            // kCommandTag_Call = 0x0a
    22.     { handle_reset, NULL },                           // kCommandTag_Reset = 0x0b
    23.     { handle_set_property, NULL },                    // kCommandTag_SetProperty = 0x0c
    24.     { handle_flash_erase_all_unsecure, NULL },        // kCommandTag_FlashEraseAllUnsecure = 0x0d
    25.     { handle_flash_program_once, NULL },              // kCommandTag_ProgramOnce = 0x0e
    26.     { handle_flash_read_once, NULL },                 // kCommandTag_ReadOnce = 0x0f
    27.     { handle_flash_read_resource, handle_data_producer }, // kCommandTag_ReadResource = 0x10
    28.     { handle_configure_memory, NULL },                    // kCommandTag_ConfigureMemory = 0x11
    29.     { handle_reliable_update, NULL },                     // kCommandTag_ReliableUpdate = 0x12
    30.     { handle_generate_key_blob, handle_key_blob_data },   // kCommandTag_GenerateKeyBlob = 0x13
    31. };
    复制代码
     如下便是用于核心命令交互的Command interface原型:
    1. //! @brief Interface to command processor operations.
    2. typedef struct CommandInterface
    3. {
    4.     // command处理控制单元初始化
    5.     status_t (*init)(void);
    6.     // command处理控制单元pump
    7.     status_t (*pump)(void);
    8.     // command服务函数查找表
    9.     const command_handler_entry_t *handlerTable;
    10.     // command处理控制单元状态数据
    11.     command_processor_data_t *stateData;
    12. } command_interface_t;

    13. //! @brief Format of command handler entry.
    14. typedef struct CommandHandlerEntry
    15. {
    16.     // command服务函数
    17.     void (*handleCommand)(uint8_t *packet, uint32_t packetLength);
    18.     // command的data级处理函数(只有少部分command有此函数)
    19.     status_t (*handleData)(bool *hasMoreData);
    20. } command_handler_entry_t;

    21. //! @brief Command processor data format.
    22. typedef struct CommandProcessorData
    23. {
    24.     // command处理控制状态机当前状态(command/data两种状态)
    25.     int32_t state;
    26.     // 指向当前处理的packet地址
    27.     uint8_t *packet;
    28.     // 当前处理的packet长度
    29.     uint32_t packetLength;
    30.     // command的data级处理控制状态数据
    31.     struct DataPhase
    32.     {
    33.         uint8_t *data;               //!< Data for data phase
    34.         uint32_t count;              //!< Remaining count to produce/consume
    35.         uint32_t address;            //!< Address for data phase
    36.         uint32_t memoryId;           //!< ID of the target memory
    37.         uint32_t dataBytesAvailable; //!< Number of bytes available at data pointer
    38.         uint8_t commandTag;          //!< Tag of command running data phase
    39.         uint8_t option;              //!< option for special command
    40.     } dataPhase;
    41.     // 指向command服务函数查找表地址
    42.     const command_handler_entry_t *handlerEntry; //! Pointer to handler table entry for packet in process
    43. } command_processor_data_t;
    复制代码







    作者:痞子衡

    签到签到
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-19 12:02 , Processed in 0.115802 second(s), 18 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表