查看: 1073|回复: 1

[分享] 如何为超级下载算法统一底层Flash驱动访问?

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

    [LV.8]以坛为家I

    3298

    主题

    6545

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32004
    最后登录
    2024-4-9
    发表于 2021-2-23 10:31:28 | 显示全部楼层 |阅读模式

    如何为超级下载算法统一底层Flash驱动访问?


    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是超级下载算法开发笔记(3)之统一FlexSPI驱动访问。


    文接上篇 《超级下载算法(RT-UFL)开发笔记(2) - 识别当前i.MXRT型号》,现在超级算法已经能够识别到当前i.MXRT型号了,下一步就是找到一套统一的底层Flash驱动函数来实现外接串行NOR Flash的基本擦写操作,这套统一的底层Flash驱动至少要在API层面做到与i.MXRT型号无关,并且调用方式统一,这样就相当方便后续的上层算法层面的逻辑设计了。


    本篇是开发笔记第三篇,咱们就重点聊聊如何为超级下载算法设计一套统一的FlexSPI驱动接口及其访问方式。


    一、找到统一的FlexSPI驱动
    我们知道i.MXRT系列内部用于连接NOR Flash的外设名字叫FlexSPI,这个外设在不同i.MXRT型号上差异很小,这对于设计通用Flash驱动函数来说方便了很多,这也是痞子衡做i.MXRT超级算法的最初动机。


    说到FlexSPI这个外设,其实就是Kinetis系列的QuadSPI外设的升级,在恩智浦MCUX SDK包里提供了一套标准的FlexSPI驱动,这个驱动写得还挺完善的,但是痞子衡并没有选择SDK标准驱动作为超级下载算法的底层Flash驱动。
    1. \SDK_2.x.x\devices\MIMXRTxxxx\drivers\fsl_flexspi.c
    2. \SDK_2.x.x\devices\MIMXRTxxxx\drivers\fsl_flexspi.h
    3. \SDK_2.x.x\components\flash\nor\flexspi\fsl_flexspi_nor_flash.c
    4. \SDK_2.x.x\components\flash\nor\flexspi\fsl_flexspi_nor_flash.h
    复制代码
    我们知道i.MXRT系列都是包含BootROM的,BootROM都支持从外部串行NOR Flash启动,这意味着BootROM中也是集成了FlexSPI驱动的(驱动源码也开源在SDK里了),BootROM里这套驱动与MCUX SDK里的驱动大体上差不多,但是细节上有差异,痞子衡最终选择了BootROM里的FlexSPI驱动作为超级算法的底层Flash驱动,原因下一节会讲。
    1. \SDK_2.x.x\middleware\mcu-boot\src\drivers\flexspi\bl_flexspi.c
    2. \SDK_2.x.x\middleware\mcu-boot\src\drivers\flexspi\bl_flexspi.h
    3. \SDK_2.x.x\middleware\mcu-boot\src\drivers\flexspi_nor\flexspi_nor_flash.c
    4. \SDK_2.x.x\middleware\mcu-boot\src\drivers\flexspi_nor\flexspi_nor_flash.h
    复制代码
    二、统一FlexSPI驱动访问方式
    现在我们虽然找到了一套看似统一的FlexSPI驱动,但事情远不是这么简单。BootROM版本的FlexSPI驱动从API接口本身而言是几乎一致的,痞子衡之前也为此写过文章 《利用i.MXRT系列ROM提供的FlexSPI driver API可轻松IAP》,但是在不同i.MXRT型号上调用方式不统一(在开放API的i.MXRT型号上API函数地址不一,在不开放API的i.MXRT型号上需要手动移植mcu-boot里的源代码),因此我们需要对所有i.MXRT型号下的BootROM FlexSPI驱动调用方式做一个统一。


    2.1 ROM API接口方式
    首先讲开放ROM API的几款i.MXRT型号(RT500/RT600/RT1060/RT1064/RT1170),这里顺便先解释一下上一节的遗留问题,为何选择BootROM版本FlexSPI驱动而不是SDK标准驱动?当然是因为有这个ROM API的存在,毕竟超级下载算法最终可执行文件越小越好,能调用ROM API可以极大地减小超级下载算法的最终代码长度。


    关于ROM API的细节,痞子衡不予赘述,我们按照如下格式准备好全部的g_bootloaderTree_imxrt宏待用(代码仅示例了i.MXRT1060)
    1. #define RT106X_ROM_API_TREE_ADDR (0x0020001cu)

    2. typedef struct _bootloader_tree_imxrt106x
    3. {
    4.     const uint32_t version;
    5.     const char *copyright;
    6.     void (*runBootloader)(void *arg);
    7.     const uint32_t reserved0;
    8.     const flexspi_nor_flash_driver_imxrt106x_t *flexspiNorDriver;
    9. } bootloader_tree_imxrt106x_t;

    10. #define g_bootloaderTree_imxrt106x (*(bootloader_tree_imxrt106x_t **)(RT106X_ROM_API_TREE_ADDR))
    复制代码
    2.2 源代码(库)接口方式
    对于没有开放ROM API的几款i.MXRT型号(RT1010/1015/1020/1024/1050),咱们就必须一一移植mcu-boot里的FlexSPI相关代码了,需移植的代码包含两部分:FlexSPI外设本身驱动,FlexSPI BSP驱动。前者移植起来倒是比较简单(直接找一个最完善的版本即可),但是后者涉及到了clock和pinmux配置,因i.MXRT型号而异,这部分代码差异较大,移植起来比较麻烦。


    FlexSPI外设本身驱动就是最终提供如下几个通用的函数即可,这部分是共用的源代码:
    1. status_t flexspi_nor_drv_flash_init(uint32_t instance, flexspi_nor_config_t *config);
    2. status_t flexspi_nor_drv_flash_page_program(uint32_t instance,
    3.                                         flexspi_nor_config_t *config,
    4.                                         uint32_t dstAddr,
    5.                                         const uint32_t *src);
    6. status_t flexspi_nor_drv_flash_erase_all(uint32_t instance, flexspi_nor_config_t *config);
    7. status_t flexspi_nor_drv_flash_erase(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, uint32_t length);
    8. status_t flexspi_nor_drv_flash_read(
    9.     uint32_t instance, flexspi_nor_config_t *config, uint32_t *dst, uint32_t start, uint32_t bytes);
    10. status_t flexspi_nor_drv_get_config(uint32_t instance, flexspi_nor_config_t *config, serial_nor_config_option_t *option);
    复制代码
    在移植FlexSPI BSP驱动过程中遇到了一个最头疼的事情,就是clock和pinmux代码需使用SDK里的基础驱动,而SDK驱动依赖i.MXRT芯片头文件,但是最终超级下载算法只有一个工程,这个工程几乎无法同时包含多个i.MXRT头文件。如果不用i.MXRT头文件,clock和pinmux代码全部改为裸写寄存器地址,工作量又太大,也不利于后期维护,最终想到的解决方案就是为每个i.MXRT型号的FlexSPI BSP驱动制作一个库工程,在库工程里各自使用自己的头文件,然后生成一个库文件作为超级下载算法工程的源文件。


    下面是示例的i.MXRT1050库文件里需提供的BSP函数列表,这也是综合多个型号SDK包里mcu-boot代码后提炼出来的:
    1. void flexspi_iomux_config_rt1050(uint32_t instance, flexspi_mem_config_t *config);
    2. void flexspi_update_padsetting_rt1050(flexspi_mem_config_t *config, uint32_t driveStrength);
    3. void flexspi_clock_config_rt1050(uint32_t instance, uint32_t freq, uint32_t sampleClkMode);
    4. status_t flexspi_set_failsafe_setting_rt1050(flexspi_mem_config_t *config);
    5. status_t flexspi_get_max_supported_freq_rt1050(uint32_t instance, uint32_t *freq, uint32_t clkMode);
    6. uint32_t CLOCK_GetCPUFreq_RT1050(void);
    7. status_t flexspi_get_clock_rt1050(uint32_t instance, flexspi_clock_type_t type, uint32_t *freq);
    8. void flexspi_clock_gate_enable_rt1050(uint32_t instance);
    9. void flexspi_clock_gate_disable_rt1050(uint32_t instance);
    10. status_t flexspi_nor_write_persistent_rt1050(const uint32_t data);
    11. status_t flexspi_nor_read_persistent_rt1050(uint32_t *data);
    复制代码
    2.3 两种不同方式的驱动统一
    现在无论是ROM API接口方式,还是源代码(库)接口方式,所有的i.MXRT型号下基础FlexSPI驱动已经准备完毕了,到了最关键的统一阶段了,我们首先可以定义一个如下ufl_target_desc_t结构体及其全局变量g_uflTargetDesc,这个结构体由FlexSPI擦写API函数指针(flexspi_nor_flash_driver_t)以及BSP函数指针(flexspi_bsp_driver_t)组成:
    1. typedef struct _target_desc
    2. {
    3.     uint32_t imxrtChipId;
    4.     flexspi_nor_flash_driver_t flashDriver;
    5.     flexspi_bsp_driver_t flexspiBsp;
    6. } ufl_target_desc_t;

    7. ufl_target_desc_t g_uflTargetDesc;
    复制代码
    然后我们定义一个ufl_fill_flash_api()函数,该函数的功能就是根据识别出来的i.MXRT型号来具体填充ufl_target_desc_t型全局结构体变量里的成员值。如果是源代码接口方式,则填入对应函数名;如果是ROM API接口方式,则根据g_bootloaderTree_imxrt宏找到对应函数地址,最终我们在g_uflTargetDesc全局变量里统一了FlexSPI驱动访问方式(下述代码仅示例了RT1050和RT1060)。
    1. static void ufl_fill_flash_api(void)
    2. {
    3.     rt_chip_id_t chipId = (rt_chip_id_t)g_uflTargetDesc.imxrtChipId;
    4.     ufl_target_desc_t *uflTargetDesc = (ufl_target_desc_t *)&g_uflTargetDesc;
    5.     switch (chipId)
    6.     {
    7.         case kChipId_RT105x:
    8.             uflTargetDesc->flashDriver.init             = flexspi_nor_drv_flash_init;
    9.             uflTargetDesc->flashDriver.page_program     = flexspi_nor_drv_flash_page_program;
    10.             uflTargetDesc->flashDriver.erase_all        = flexspi_nor_drv_flash_erase_all;
    11.             uflTargetDesc->flashDriver.erase            = flexspi_nor_drv_flash_erase;
    12.             uflTargetDesc->flashDriver.read             = flexspi_nor_drv_flash_read;
    13.             uflTargetDesc->flashDriver.set_clock_source = NULL;
    14.             uflTargetDesc->flashDriver.get_config       = flexspi_nor_drv_get_config;

    15.             uflTargetDesc->flexspiBsp.flexspi_iomux_config         = flexspi_iomux_config_rt1050;
    16.             uflTargetDesc->flexspiBsp.flexspi_update_padsetting    = flexspi_update_padsetting_rt1050;
    17.             uflTargetDesc->flexspiBsp.flexspi_clock_config         = flexspi_clock_config_rt1050;
    18.             uflTargetDesc->flexspiBsp.flexspi_set_failsafe_setting = flexspi_set_failsafe_setting_rt1050;
    19.             uflTargetDesc->flexspiBsp.CLOCK_GetCPUFreq             = CLOCK_GetCPUFreq_RT1050;
    20.             uflTargetDesc->flexspiBsp.flexspi_get_max_supported_freq = flexspi_get_max_supported_freq_rt1050;
    21.             uflTargetDesc->flexspiBsp.flexspi_clock_gate_enable    = flexspi_clock_gate_enable_rt1050;
    22.             uflTargetDesc->flexspiBsp.flexspi_clock_gate_disable   = flexspi_clock_gate_disable_rt1050;
    23.             uflTargetDesc->flexspiBsp.flexspi_nor_write_persistent = flexspi_nor_write_persistent_rt1050;
    24.             uflTargetDesc->flexspiBsp.flexspi_get_clock            = flexspi_get_clock_rt1050;
    25.             uflTargetDesc->flexspiBsp.flexspi_nor_read_persistent  = flexspi_nor_read_persistent_rt1050;
    26.             break;

    27.         case kChipId_RT106x:
    28.             uflTargetDesc->flashDriver.init             = g_bootloaderTree_imxrt106x->flexspiNorDriver->init;
    29.             uflTargetDesc->flashDriver.page_program     = g_bootloaderTree_imxrt106x->flexspiNorDriver->program;
    30.             uflTargetDesc->flashDriver.erase_all        = g_bootloaderTree_imxrt106x->flexspiNorDriver->erase_all;
    31.             uflTargetDesc->flashDriver.erase            = g_bootloaderTree_imxrt106x->flexspiNorDriver->erase;
    32.             uflTargetDesc->flashDriver.read             = g_bootloaderTree_imxrt106x->flexspiNorDriver->read;
    33.             uflTargetDesc->flashDriver.set_clock_source = NULL;
    34.             uflTargetDesc->flashDriver.get_config       = g_bootloaderTree_imxrt106x->flexspiNorDriver->get_config;
    35.             break;

    36.         case kChipId_Invalid:
    37.         default:
    38.             break;
    39.     }
    40. }
    复制代码
    有了g_uflTargetDesc全局变量,此时再包一层API驱动给最终下载算法上层逻辑调用就非常简单了。
    1. status_t flexspi_nor_flash_init(uint32_t instance, flexspi_nor_config_t *config)
    2. {
    3.     return g_uflTargetDesc.flashDriver.init(instance, config);
    4. }

    5. void flexspi_iomux_config(uint32_t instance, flexspi_mem_config_t *config)
    6. {
    7.     g_uflTargetDesc.flexspiBsp.flexspi_iomux_config(instance, config);
    8. }
    复制代码




    签到签到
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2021-12-23 09:57
  • 签到天数: 1587 天

    [LV.Master]伴坛终老

    5

    主题

    3046

    帖子

    23

    金牌会员

    Rank: 6Rank: 6

    积分
    8200
    最后登录
    2024-4-17
    发表于 2021-2-23 18:22:57 | 显示全部楼层
    收藏+顶一下
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-20 19:28 , Processed in 0.110884 second(s), 19 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

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