0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程

冬至子 来源:痞子衡嵌入式 作者:痞子衡 2023-06-02 17:43 次阅读

一、整体初始化流程

我们知道外部串行NOR Flash是接到i.MXRT的FlexSPI外设引脚上,有时串行NOR Flash启动也叫FlexSPI NOR启动。关于FlexSPI NOR启动流程,i.MXRT1050参考手册System Boot章节有如下流图,蓝框之外的流程属于常规i.MXRT启动XIP App流程,是个通用流程。蓝框之内才是具体FlexSPI初始化步骤,这个步骤概括得比较精炼。

图片

为了让大家对FlexSPI NOR设备启动初始化流程有个更具体的概念,痞子衡重新画了一张更详细的流程图,图中灰底框里描述得是FlexSPI初始化流程,痞子衡将其分解成了六步,我们有必要深入这六步初始化流程。

图片

二、分解初始化流程

2.1 复位Flash芯片(可选)

第一步是尝试复位Flash芯片,这步是可选的,在fuse_0x6e0[7]里配置,默认是不使能的。复位Flash目的是为了让Flash处于一个确定的初始状态,方便i.MXRT BootROM去配置访问。为什么要强调Flash的初始状态,因为很多时候i.MXRT未必是冷启动(上电启动),也有可能是软复位启动(比如调用NVIC_SystemReset),这时候外部Flash已经被软复位前执行过的BootROM甚至用户App配置过,因此Flash的状态可能不是上电初始状态(一般来说板级设计里Flash的RESET#引脚要么悬空,要么连接i.MXRT的POR#引脚),这可能会影响软复位后BootROM去再次配置启动这块不定态的Flash。

fuse 0x6e0[7] - FLEXSPI_RESET_PIN_EN

图片

正常的Flash都提供了RESET#引脚来实现跟上电复位一样的功能,对于普通8-pin的QSPI Flash,这个RESET#引脚往往是跟信号线IO3复用的(仅在QE bit没使能情况下有效),而对于16-pin的QSPI Flash或者HyperFlash,其RESET#引脚都是独立的。

图片

BootROM就是借助了Flash的RESET#引脚来实现的复位操作,实现代码比较简单,i.MXRT1050 BootROM直接指定了GPIO1[9]当做复位信号线,板级设计里需要你将GPIO1[9]连到Flash的RESET#引脚,然后BootROM就是简单地拉低GPIO1[9]即可。RESET#信号都是低电平有效,BootROM直接拉低这个信号持续250us,这个低电平持续时间对于复位来说是够够的,很多Flash数据手册里其实仅要求几us即可。

  • 备注:对于BootROM的Flash复位功能来说,主要适用有独立RESET#引脚的Flash。
#define RESET_PAD_IDX       kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_09
#define RESET_PIN_MUX       IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(5)
#define RESET_PIN_GPIO      GPIO1
#define RESET_PIN_INDEX     9

if ((OCOTP- >MISC_CONF1 & 0x80) > > 7)
{
    // Set pinmux as GPIO
    IOMUXC- >SW_MUX_CTL_PAD[RESET_PAD_IDX] = RESET_PIN_MUX;
    // Set GPIO to output mode
    RESET_PIN_GPIO- >GDIR |= (1U<

2.2 准备初始FDCB配置块

第二步是准备一个初始的FDCB配置块(即flexspi_nor_config_t,大小为512字节),这个初始FDCB配置块将被用来做FlexSPI外设的第一次初始化,目的是为了能够保证FlexSPI初始化之后CPU能够使用AHB方式正常读取Flash(访问性能不要求最高,但求稳定访问)。这个初始FDCB并不是一个完全定死的配置块,部分值也是根据fuse来配置的,一共有三处fuse位置,其中最重要的是FLASH_TYPE:

fuse 0x440[20]    - QSPI_2ND_BOOTPIN_ENABLE,决定是否启动第二组FlexSPI pinmux
fuse 0x450[10:8]  - FLASH_TYPE,决定当前连接的Flash类型
fuse 0x470[30:24] - DELAY_CELL_NUM,设置Flash读访问时序数据线有效时间

图片

初始FDCB配置块中仅给memConfig设了值,这个memConfig才是用于配置FlexSPI外设本身。如下部分赋值是固定的FDCB设置,不受fuse影响,从这个固定配置你可以看到,BootROM假定了所有外接Flash都是128MB,且访问时钟(SCK)速度能支持30MHz,不要对这个假定感到焦虑,它只是用于FlexSPI第一次初始化,目的只求能正常访问Flash前4KB即可:

flexspi_nor_config_t config;
memset(config, 0, sizeof(config));

// 公共的FDCB配置
config.memConfig.tag           = FLEXSPI_CFG_BLK_TAG;
config.memConfig.version       = FLEXSPI_CFG_BLK_VERSION;
config.memConfig.deviceType    = kFlexSpiDeviceType_SerialNOR;
config.memConfig.sflashA1Size  = 128UL*1024*1024;
config.memConfig.serialClkFreq = kFlexSpiSerialClk_30MHz;
config.memConfig.dataHoldTime  = 3;
config.memConfig.dataSetupTime = 3;
config.memConfig.timeoutInMs   = 1000;

然后便是从fuse里获取flashType,根据具体flashType来对初始FDCB配置块做进一步动态赋值,这进一步赋值才用于区分不同Flash种类(Pad数量、DQS信号属性、最重要的lookupTable等)。

// 从fuse里获取flash类型
uint32_t flashType;
if ((OCOTP- >CFG3 & 0x100000) > > 20)
{
    flashType = 7;
}
else
{
    flashType = (OCOTP- >CFG4 & 0x700) > > 8;
}

图片

上图中最重要的FDCB赋值是config.memConfig.lookupTable,它是FlexSPI外设需要的核心配置,有了这个配置,CPU便可以直接从AHB总线读取Flash的内容,因为FlexSPI会自动解析AHB总线读请求然后翻译成具体FlexSPI读时序,底层读时序需要的命令、地址字节数、DUMMY周期都在lookupTable里。BootROM预存了如下6大类Flash的lookupTable:

// Dedicated 3Byte Address Read(0x03), 24bit address
static const uint32_t s_dedicated3bRead[4]   = {
    FLEXSPI_LUT_SEQ(CMD_SDR,  FLEXSPI_1PAD, 0x03, RADDR_SDR, FLEXSPI_1PAD, 0x18),
    FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_1PAD, 0x04, STOP,      FLEXSPI_1PAD, 0),
    0,
    0
};

// Dedicated 4Byte Address Read(0x13), 32 bit address
static const uint32_t s_dedicated4bRead[4]   = {
    FLEXSPI_LUT_SEQ(CMD_SDR,   FLEXSPI_1PAD, 0x13, RADDR_SDR, FLEXSPI_1PAD, 0x20),
    FLEXSPI_LUT_SEQ(READ_SDR,  FLEXSPI_1PAD, 0x04, STOP,      FLEXSPI_1PAD, 0),
    0,
    0
};
// HyperFlash Read
static const uint32_t s_hyperflashRead[4]    = {
    FLEXSPI_LUT_SEQ(CMD_DDR,   FLEXSPI_8PAD, 0xA0, RADDR_DDR,      FLEXSPI_8PAD, 0x18),
    FLEXSPI_LUT_SEQ(CADDR_DDR, FLEXSPI_8PAD, 0x10, DUMMY_RWDS_DDR, FLEXSPI_8PAD, 0x0c),
    FLEXSPI_LUT_SEQ(READ_DDR,  FLEXSPI_8PAD, 0x04, STOP,           FLEXSPI_8PAD, 0),
    0
};

// MXIC Octal DDR read
static const uint32_t s_mxicOctDdrRead[4]    = {
    FLEXSPI_LUT_SEQ(CMD_DDR,   FLEXSPI_8PAD, 0xEE, CMD_DDR,   FLEXSPI_8PAD, 0x11),
    FLEXSPI_LUT_SEQ(RADDR_DDR, FLEXSPI_8PAD, 0x20, DUMMY_DDR, FLEXSPI_8PAD, 0xc),
    FLEXSPI_LUT_SEQ(READ_DDR,  FLEXSPI_8PAD, 0x04, STOP,      FLEXSPI_8PAD, 0),
    0
};

// Micron Octal DDR read
static const uint32_t s_micronOctDdrRead[4]  = {
    FLEXSPI_LUT_SEQ(CMD_SDR,   FLEXSPI_8PAD, 0xFD, RADDR_DDR, FLEXSPI_8PAD, 0x20),
    FLEXSPI_LUT_SEQ(DUMMY_DDR, FLEXSPI_8PAD, 0x8,  READ_DDR,  FLEXSPI_8PAD, 0x04),
    0,
    0
};
// Adesto Octal DDR read
static const uint32_t s_adestoOctDdrRead[4]  = {
    FLEXSPI_LUT_SEQ(CMD_SDR,   FLEXSPI_8PAD, 0x0B, RADDR_DDR, FLEXSPI_8PAD, 0x20),
    FLEXSPI_LUT_SEQ(DUMMY_DDR, FLEXSPI_8PAD, 0x8,  READ_DDR,  FLEXSPI_8PAD, 0x04),
    0,
    0
};

2.3 第一次FlexSPI初始化

第三步就是利用上述配置完成的初始FDCB块对FlexSPI外设进行第一次初始化,就是下面代码,这个流程跟官方SDK里的flexspi_nor_flash_init()大同小异,这里不予具体展开。如果在这里初始化就返回失败(这里一般不会失败,因为仅仅是FlexSPI外设自身初始化,并不涉及操作外部Flash芯片的动作),BootROM则直接退出FlexSPI NOR设备启动,转入SDP下载

#define FLEXSPI_INSTANCE    0
uint32_t instance = FLEXSPI_INSTANCE;

status_t status = flexspi_init(instance, (flexspi_mem_config_t *)(&config));
if (status != kStatus_Success)
{
    return status;
}
flexspi_update_lut(instance, 0, &config.memConfig.lookupTable, 1);

2.4 若干善后工作

上述第一次FlexSPI初始化一般都会成功的,但这并不代表fuse里的flashType等配置跟板子上Flash型号是匹配的,也就是说初始FDCB配置块此时还没有被充分验证其是否适用板载Flash型号。

FlexSPI第一次初始化结束后,为了保证后续能正常AHB访问,BootROM里做了一些善后工作,主要是两件事:

  1. 做一些访问前的延时:根绝fuse 0x450[3:2] - HOLD TIME来调用microseconds_delay()做延时,以使FlexSPI外设完全准备好。
  2. 做一次无效AHB访问:类似这样的代码 volatile uint32_t dummy = *(uint32_t *)0x60000000;,无效AHB读可以使Flash退出continuous read模式

2.5 获取用户FDCB配置块

善后工作结束之后,此时CPU应该可以通过AHB正常访问Flash了,这个阶段我们只需要从Flash的偏移0地址处读取用户FDCB,验证用户FDCB是否存在,这里才是对前面初始FDCB配置块以及第一次FlexSPI外设初始化的真正考验。

验证用户FDCB是否存在就是简单读取FDCB的前四个字节(tag),验证这个tag是否合法。如果第一次验证tag不成功(有可能是FlexSPI配置不正确,也有可能是用户FDCB不存在),会尝试做一次三字节地址切换到四字节地址的LUT更新(仅适用QSPI Flash),然后做第二次tag读取验证,如果此时还是验证失败(大概率是不存在用户FDCB了),BootROM则直接退出FlexSPI NOR设备启动,转入SDP下载。

#define FlexSPI_AMBA_BASE      (0x60000000U)
#define FLASH_BASE             FlexSPI_AMBA_BASE

// 使用三字节地址的LUT对Flash进行初次AHB访问
flexspi_clear_cache(FLEXSPI_INSTANCE);
flexspi_nor_config_t *pConfig = (flexspi_nor_config_t *)FLASH_BASE;
if (pConfig- >memConfig.tag != FLEXSPI_CFG_BLK_TAG)
{
    // 因为拿不到用户FDCB的tag,尝试切换使用四字节地址的LUT
    if (flashType == 0)
    {
        flexspi_update_lut(FLEXSPI_INSTANCE, 0, s_basic4bRead, 1);
    }
    flexspi_clear_cache(FLEXSPI_INSTANCE);
    pConfig = (flexspi_nor_config_t *)FLASH_BASE;
}

// 对Flash进行第二次AHB访问,再次确认能否拿到用户FDCB的tag
if (pConfig- >memConfig.tag != FLEXSPI_CFG_BLK_TAG)
{
    return kStatus_Fail;
}

上面代码里有flexspi_clear_cache()操作,这个其实就是利用FLEXSPI0->MCR0[SWRESET]做一个外设级别的软复位,另外代码里还涉及到一个四字节地址QSPI Flash的LUT表,即如下所示:

// Basic read with 32bit address
static const uint32_t s_basic4bRead[4]   = {
    FLEXSPI_LUT_SEQ(CMD_SDR,  FLEXSPI_1PAD,  0x03, RADDR_SDR, FLEXSPI_1PAD, 0x20), 
    FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_1PAD,  0x04, STOP,      FLEXSPI_1PAD, 0),
    0,
    0
};

2.6 第二次FlexSPI初始化

到了这里,基本代表第一次FlexSPI初始化是正确且可用的,并且能够拿到有效的用户FDCB配置块。这时候就是利用用户FDCB配置块对FlexSPI外设做第二次初始化,初始化代码流程跟第一次初始化是一模一样的。

这个第二次初始化是非常有必要的,因为它反映了用户的真实需求,用户FDCB配置块里会准确描述板载Flash的全面特性(访问速度,真实存储空间大小,特殊定制LUT等等),这些信息必须由用户来提供。

需要注意的是,第二次FlexSPI初始化返回成功并不代表用户FDCB配置块一定就是正确的,还是那句话,这仅仅是对FlexSPI外设自身的初始化。后续常规App解析流程里才是对这个用户FDCB配置块的真正考验。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • GPIO
    +关注

    关注

    16

    文章

    1196

    浏览量

    51897
  • AHB总线
    +关注

    关注

    0

    文章

    18

    浏览量

    9468
  • QSPI接口
    +关注

    关注

    0

    文章

    14

    浏览量

    3336
收藏 人收藏

    评论

    相关推荐

    i.MXRTxxx系列ROM灵活的串行NOR Flash启动硬复位引脚选择

    篇非常详细的文章 《深入i.MXRT1050系列ROM串行
    发表于 12-21 06:34

    IAR开发环境下i.MXRT串行NOR Flash下载算法设计

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是IAR开发环境下i.MXRT串行NOR Flash下载算法设计。  在i.MX
    发表于 01-26 07:46

    i.MXRT上使能NOR Flash的Continuous read模式在软复位后无法正常启动问题的解决

    串行NOR Flash的Continuous read模式下软复位后i.MXRT无法启动问题解决方案之RESET#》,利用RESET#引
    发表于 01-26 06:52

    系统时钟配置不当会导致i.MXRT1xxx系列下OTFAD加密启动失败的解决办法

    型号(RT1050/RT0160/RT1020)的硬件解密外设名字叫BEE,这个外设主要是配合FlexSPI外设去实现外接串行NOR Flash在线解密XIP执行用的。而到了最近的
    发表于 02-08 06:09

    FPGA的ROM初始化问题讨论

    本文讨论FPGA的ROM初始化问题,详细介绍mit文件的创建与使用。利用FPGA实现的ROM只能认为器件处于用户状态时具备ROM功能。使用时不必要刻意划分,而
    发表于 02-08 14:24 5467次阅读
    FPGA的<b class='flag-5'>ROM</b><b class='flag-5'>初始化</b>问题讨论

    浅析Keil MDK下串行Flash的下载算法设计

    今天给大家介绍的是 Keil MDK 工具下 i.MXRT串行 NOR Flash 下载算法设计。 在 i.MXRT 硬件那些事
    的头像 发表于 12-23 13:15 1421次阅读

    J-Link工具下i.MXRT串行NOR Flash下载算法设计

    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是 J-Link 工具下 i.MXRT串行 NOR Flash 下载算法设计。 一、J-Link 各版本对
    的头像 发表于 12-08 10:07 1008次阅读

    Flash不支持SFDP,如何下载适用i.MXRT

    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是导致串行 NOR Flashi.MXRT 下无法正常下载 / 启动的常
    的头像 发表于 10-30 10:30 667次阅读

    i.MXRT系列ROM API设计

    的 FlexSPI driver API 可轻松 IAP》、《其实 i.MXRT1050,1020,1015 系列 ROM 也提供了 FlexSPI driver API》 基本把 i.MXR
    的头像 发表于 10-30 10:52 553次阅读

    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU硬件那些事(2.5)- 串行NOR Flash下载算法(IAR EWARM篇)...

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是IAR开发环境下i.MXRT串行NOR Flash下载算法设计。  在i.MX
    发表于 12-02 09:06 7次下载
    痞子衡嵌入式:恩智浦<b class='flag-5'>i</b>.MX RT1xxx<b class='flag-5'>系列</b>MCU硬件那些事(2.5)- <b class='flag-5'>串行</b><b class='flag-5'>NOR</b> <b class='flag-5'>Flash</b>下载算法(IAR EWARM篇)...

    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(11.1)- FlexSPI NOR连接方式大全(RT1015/1020/1050)

    非易失性存储器,因此在系统设计时为i.MXRT搭配一块存放应用程序代码的存储器是头等大事。i.MXRT支持启动的外部存储器类型众多,其中通过FlexSPI接口连接串行
    发表于 12-06 10:51 8次下载
    痞子衡嵌入式:恩智浦<b class='flag-5'>i</b>.MX RT1xxx<b class='flag-5'>系列</b>MCU<b class='flag-5'>启动</b>那些事(11.1)- FlexSPI <b class='flag-5'>NOR</b>连接方式大全(RT1015/1020/<b class='flag-5'>1050</b>)

    i.MXRT1170上串行NOR Flash双程序可交替启动设计

    i.MXRT10xx 一样,这里要聊的还是在一片挂载在 FlexSPI 上的串行 NOR Flash 里做冗余/双程序设计,就是下图中的 image L 和 image H,不涉及
    的头像 发表于 04-29 15:23 1021次阅读

    i.MXRTxxx系列i.MXRT11xx系列双程序启动细节差异

    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT500/600上串行NOR Flash双程序可交替启动设计。
    的头像 发表于 05-06 12:44 2173次阅读

    i.MX RT500/600系列串行NOR Flash双程序可交替启动设计

    i.MX RT500/600系列串行NOR Flash双程序可交替启动设计
    的头像 发表于 10-27 09:36 452次阅读
    <b class='flag-5'>i</b>.MX RT500/600<b class='flag-5'>系列</b>上<b class='flag-5'>串行</b><b class='flag-5'>NOR</b> <b class='flag-5'>Flash</b>双程序可交替<b class='flag-5'>启动</b>设计

    恩智浦i.MX RT1060/1010上串行NOR Flash冗余程序启动设计

    恩智浦i.MX RT1060/1010上串行NOR Flash冗余程序启动设计
    的头像 发表于 09-26 16:53 699次阅读
    恩智浦<b class='flag-5'>i</b>.MX RT1060/1010上<b class='flag-5'>串行</b><b class='flag-5'>NOR</b> <b class='flag-5'>Flash</b>冗余程序<b class='flag-5'>启动</b>设计