查看: 727|回复: 2

[分享] 在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle (...

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

    [LV.8]以坛为家I

    3309

    主题

    6558

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32140
    最后登录
    2024-5-9
    发表于 2022-11-18 10:46:43 | 显示全部楼层 |阅读模式
    在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle (以IS25WP128为例)

    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是Flash工作频率与Dummy Cycle的联系。
      上一篇文章 《从头开始认识i.MXRT启动头FDCB里的lookupTable》,痞子衡带大家从头梳理了下i.MXRT下启动头FDCB里lookupTable的设计与实现细节,在这个过程中也简单跟大家介绍了串行NOR Flash的工作模式(主要是Fast Read Quad I/O),今天痞子衡在FDCB设置范畴下跟大家再进一步地探讨Flash工作频率与Dummy Cycle设置问题。

    一、引入Flash工作频率与Dummy Cycle对应关系问题
      今天我们以i.MXRT1170-EVK上的板载Flash为例,其具体型号为IS25WP128-JBLE,在Flash数据手册特性介绍里可以看到这颗Flash最高可以运行在133MHz频率下(SDR模式),并且其Dummy Cycle也是可选的,那么Dummy Cycle是不是可以任意配呢?答案既是也不是,痞子衡先卖个关子。

      让我们再来回顾下这颗Flash的Fast Read Quad I/O时序图,从时序图里可以看到Dummy Cycle默认是6(包括Mode Bits时序一共6个SCK时钟周期),那么默认的6个Dummy Cycle是不是适用全部的Flash工作频率呢?咱们继续看Flash数据手册。

      在数据手册里找到了下面这张表,Read Dummy Cycle与最大工作频率的联系,从表里可以看到当Flash工作在Fast Read Quad I/O模式时,默认的6个Dummy Cycle适用的最大工作频率是104MHz,即104MHz工作频率及以下均可以使用默认的6个Dummy Cycle。如果工作频率高于104MHz,Dummy Cycle应相应调大,比如133MHz工作频率需对应至少9个Dummy Cycle。

    二、如何在FDCB里设置FlexSPI的Dummy Cycle?
      根据上面的分析,如果我们希望i.MXRT1170-EVK上这颗IS25WP128-JBLE启动后工作在133MHz,那么我们需要在FDCB里至少配置9个Dummy Cycle。因此下述FDCB启动头里 FLASH_DUMMY_CYCLES 宏应设为9, qspiflash_config.memConfig.serialClkFreq 应改为 kFlexSpiSerialClk_133MHz,这样的设置对于i.MXRT端是足够的,因为更改后FlexSPI外设确实可以输出133MHz的SCK,并且按9个Dummy Cycle的时序去读Flash。

      但是把这样的FDCB启动头直接下载进Flash后发现i.MXRT无法从Flash正常启动,因为Flash端的Dummy Cycle还是默认的6个SCK周期,上面的FDCB仅仅是调整了FlexSPI输出,并不会同步调整Flash端,此时主从两端Dummy Cycle数不匹配,时序错乱了,传输数据发生了错位,应用程序当然无法启动。

      所以Flash这边需要其他的方式设置好Dummy Cycle后,上述方式更改的FDCB启动头才能正常使用。

      有一个现象需要特别说明一下,如果我们直接修改 qspiflash_config.memConfig.serialClkFreq 到 kFlexSpiSerialClk_133MHz, 但是 FLASH_DUMMY_CYCLES 依旧保持默认的6,这样的FDCB头下载进Flash也可以正常工作的,虽然有点违反Flash数据手册里的Dummy Cycle规范,但实测下来似乎是没问题的,那是不是意味着我们根本不需要动默认的Dummy Cycle?其实不是的,当我们使能Flash的 continuous read mode 的时候,Dummy Cycle不规范就会出问题,关于这点痞子衡会单独写一篇文章细聊。

    三、如何更改Flash里的Dummy Cycle?
      那么Flash里的Dummy Cycle到底怎么改呢?这需要我们继续看Flash数据手册,IS25WP128-JBLE内部有个8bit的Read Register,其bit6-bit3是Dummy Cycles设置,可设范围是1-15,并且在寄存器类型里可以看到其有易失性和非易失性两种属性,也就是说我们既可以临时地改Dummy Cycle(掉电即恢复默认6),也可以永久地改Dummy Cycle(掉电仍保持上一次设置)。

      在IS25WP128-JBLE的指令集表里,可以看到有专门写Read Register的指令,SRPNV指令是非易失性方式地写Read Register,SRPV指令是易失性方式去写Read Register。

      分析到这里,问题就变成到底是使用一个额外的小工程(比如借助SDK里的flexspi example稍微更改下代码)以SRPNV指令去永久性更改下Flash里的Dummy Cycle再用作i.MXRT启动,还是i.MXRT每次启动时直接借助FDCB启动头里的设置用SRPV指令临时地更改Flash的Dummy Cycle?从灵活性角度,痞子衡推荐第二种方式,那么在FDCB里应该怎么做?痞子衡直接给答案:
    1. // 设置Dummy Cycle数
    2. #define FLASH_DUMMY_CYCLES      9
    3. // 写Read Register时序在LUT中的index(可自定义位置,但不要占BootROM预设的几个时序位置)
    4. #define CMD_LUT_SEQ_IDX_SET_READ_PARAM 7
    5. // BootROM中预设的LUT命令时序的index
    6. #define CMD_LUT_SEQ_IDX_READ           0
    7. #define CMD_LUT_SEQ_IDX_READSTATUS     1
    8. #define CMD_LUT_SEQ_IDX_WRITEENABLE    3

    9. const flexspi_nor_config_t qspiflash_config = {
    10.     .memConfig =
    11.         {
    12.             .tag              = FLEXSPI_CFG_BLK_TAG,
    13.             .version          = FLEXSPI_CFG_BLK_VERSION,
    14.             .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad,
    15.             .csHoldTime       = 3u,
    16.             .csSetupTime      = 3u,
    17.             // Enable Safe configuration
    18.             .controllerMiscOption = 0x10,
    19.             .deviceType           = kFlexSpiDeviceType_SerialNOR,
    20.             .sflashPadType        = kSerialFlash_4Pads,
    21.             .serialClkFreq        = kFlexSpiSerialClk_133MHz,
    22.             .sflashA1Size         = 16u * 1024u * 1024u,
    23.             // 使能Flash寄存器配置操作
    24.             .configCmdEnable = 1u,
    25.             .configModeType[0] = kDeviceConfigCmdType_Generic,
    26.             // 指示Flash寄存器配置时序在LUT中index
    27.             .configCmdSeqs[0] =
    28.                 {
    29.                     .seqNum = 1,
    30.                     .seqId = CMD_LUT_SEQ_IDX_SET_READ_PARAM,
    31.                     .reserved = 0,
    32.                 },
    33.             // 设定Flash寄存器配置值(这里就是写入Read Register的值)
    34.             .configCmdArgs[0] = FLASH_DUMMY_CYCLES << 3,
    35.             .lookupTable =
    36.                 {
    37.                     // Fast Read Quad I/O
    38.                     [4*CMD_LUT_SEQ_IDX_READ]               = FLEXSPI_LUT_SEQ(CMD_SDR,   FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18),
    39.                     [4*CMD_LUT_SEQ_IDX_READ + 1]           = FLEXSPI_LUT_SEQ(MODE8_SDR, FLEXSPI_4PAD, 0x00, DUMMY_SDR, FLEXSPI_4PAD, FLASH_DUMMY_CYCLES-2),
    40.                     [4*CMD_LUT_SEQ_IDX_READ + 2]           = FLEXSPI_LUT_SEQ(READ_SDR,  FLEXSPI_4PAD, 0x04, STOP,      FLEXSPI_1PAD, 0x00),
    41.                   
    42.                     // READ STATUS REGISTER
    43.                     [4*CMD_LUT_SEQ_IDX_READSTATUS]         = FLEXSPI_LUT_SEQ(CMD_SDR,   FLEXSPI_1PAD, 0x05, READ_SDR,  FLEXSPI_1PAD, 0x01),
    44.                     [4*CMD_LUT_SEQ_IDX_READSTATUS + 1]     = FLEXSPI_LUT_SEQ(STOP,      FLEXSPI_1PAD, 0x00, 0, 0, 0),
    45.                   
    46.                     // WRTIE ENABLE
    47.                     [4*CMD_LUT_SEQ_IDX_WRITEENABLE]        = FLEXSPI_LUT_SEQ(CMD_SDR,   FLEXSPI_1PAD, 0x06, STOP,      FLEXSPI_1PAD, 0x00),

    48.                     // Flash寄存器配置时序(这个时序需要上面READ STATUS, WRITE ENABLE的配合)
    49.                     [4*CMD_LUT_SEQ_IDX_SET_READ_PARAM]     = FLEXSPI_LUT_SEQ(CMD_SDR,   FLEXSPI_1PAD, 0x63, WRITE_SDR, FLEXSPI_1PAD, 0x01),
    50.                     [4*CMD_LUT_SEQ_IDX_SET_READ_PARAM + 1] = FLEXSPI_LUT_SEQ(STOP,      FLEXSPI_1PAD, 0x00, 0, 0, 0),
    51.                 },
    52.         },
    53.     .pageSize           = 256u,
    54.     .sectorSize         = 4u * 1024u,
    55.     .blockSize          = 256u * 1024u,
    56.     .isUniformBlockSize = false,
    57. };
    复制代码
    四、BootROM对FDCB里Flash寄存器配置的处理

      说到BootROM对FDCB的处理,大家有必要先回顾下痞子衡写过的一篇旧文 《深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程》,文章虽然是针对i.MXRT1050写的,但基本流程也差不多适用i.MXRT1170,在文中的 2.6 小节第二次初始化里,我们在上面FDCB里的关于Flash寄存器的额外配置操作才会被处理,代码大致如下:
    1. status_t flexspi_init(uint32_t instance, flexspi_mem_config_t *config)
    2. {
    3.     status_t status = kStatus_InvalidArgument;

    4.     // 决定是否用30MHz SDR的安全时钟来做FlexSPI初始化
    5.     bool needSafeFreq = (config->deviceModeCfgEnable || config->configCmdEnable) &&
    6.                           ((config->controllerMiscOption & (1 << kFlexSpiMiscOffset_SafeConfigFreqEnable)));
    7.     flexspi_clock_gate_disable(instance);
    8.     flexspi_iomux_config(instance, config);
    9.     if (needSafeFreq)
    10.     {
    11.         flexspi_clock_config(instance, kFlexSpiSerialClk_SafeFreq, kFlexSpiClk_SDR);
    12.     }

    13.     flexspi_clock_gate_enable(instance);
    14.     // 省略代码片段,FlexSPI模块本身的完整初始化
    15.     // ...
    16.     flexspi_swreset(base);
    17.    
    18.     // 实现Flash配置寄存器写入操作
    19.     if (config->configCmdEnable)
    20.     {
    21.         // Port A1/A2/B1/B2如使能则全写一遍
    22.         uint32_t baseAddr = 0;
    23.         uint32_t *flashSizeStart = &config->sflashA1Size;
    24.         for (uint32_t index = 0; index < 4; index++)
    25.         {
    26.             uint32_t currentFlashSize = *flashSizeStart++;
    27.             if (currentFlashSize > 0)
    28.             {
    29.                 flexspi_device_cmd_config(instance, config, baseAddr);
    30.                 baseAddr += currentFlashSize;
    31.             }
    32.         }
    33.     }

    34.     if (needSafeFreq)
    35.     {
    36.         // 重新配回指定的FlexSPI时钟
    37.         flexspi_clock_config(instance, config->serialClkFreq, flexspi_is_ddr_mode_enable(config));
    38.     }

    39.     return status;
    40. }

    41. void flexspi_device_cmd_config(uint32_t instance, flexspi_mem_config_t *config, uint32_t baseAddr)
    42. {
    43.     FLEXSPI_Type *base = flexspi_get_module_base(instance);
    44.     flexspi_xfer_t flashXfer;
    45.     flashXfer.operation = kFlexSpiOperation_Config;
    46.     flashXfer.baseAddress = baseAddr;
    47.     flashXfer.isParallelModeEnable = false;
    48.     flashXfer.txSize = 4;

    49.     for (uint32_t index = 0; index < 3; index++)
    50.     {
    51.         if (config->configCmdSeqs[index].seqId > 0)
    52.         {
    53.             // If device is working under DPI/QPI/OPI mode, ignore SPI2XPI command
    54.             uint32_t read_cmd_pads = (base->LUT[0] >> 8) & 0x03;
    55.             if ((read_cmd_pads > FLEXSPI_1PAD) && (config->configModeType[index] == kDeviceConfigCmdType_Spi2Xpi))
    56.             {
    57.                 continue;
    58.             }

    59.             flashXfer.seqId = config->configCmdSeqs[index].seqId;
    60.             flashXfer.seqNum = config->configCmdSeqs[index].seqNum;
    61.             flashXfer.txBuffer = &config->configCmdArgs[index];

    62.             // 这里需要调用WRITE ENABLE指令
    63.             flexspi_device_write_enable(instance, config, false, baseAddr);

    64.             flexspi_update_lut(instance, 1, &config->lookupTable[4 * flashXfer.seqId], flashXfer.seqNum);
    65.             flashXfer.seqId = 1;
    66.             flexspi_command_xfer(instance, &flashXfer);

    67.             if ((!config->waitTimeCfgCommands) &&
    68.                 (config->configModeType[index] != (uint8_t)kDeviceConfigCmdType_Spi2Xpi) &&
    69.                 (config->configModeType[index] != (uint8_t)kDeviceConfigCmdType_Xpi2Spi))
    70.             {
    71.                 // 这里需要调用READ STATUS指令
    72.                 flexspi_device_wait_busy(instance, config, false, baseAddr);
    73.             }
    74.             else
    75.             {
    76.                 flexspi_sw_delay_us(config->waitTimeCfgCommands * 100UL);
    77.             }
    78.         }
    79.     }
    80. }
    复制代码
    至此,Flash工作频率与Dummy Cycle的联系痞子衡便介绍完毕了,掌声在哪里~~~






    签到签到
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    5

    帖子

    0

    新手上路

    Rank: 1

    积分
    32
    最后登录
    2022-12-8
    发表于 2022-11-22 09:52:10 | 显示全部楼层
    给掌声。。。。。。
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    5

    帖子

    0

    新手上路

    Rank: 1

    积分
    32
    最后登录
    2022-12-8
    发表于 2022-11-22 09:56:03 | 显示全部楼层
    <深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程>在哪里,咋没找到嘛
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2024-5-9 17:19 , Processed in 0.130888 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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