痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle

  大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是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是不是可以任意配呢?答案既是也不是,痞子衡先卖个关子。

痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle

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

痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle

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

痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设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启动头才能正常使用。

痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle

  有一个现象需要特别说明一下,如果我们直接修改 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(掉电仍保持上一次设置)。

痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle

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

痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle

痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle

  分析到这里,问题就变成到底是使用一个额外的小工程(比如借助SDK里的flexspi example稍微更改下代码)以SRPNV指令去永久性更改下Flash里的Dummy Cycle再用作i.MXRT启动,还是i.MXRT每次启动时直接借助FDCB启动头里的设置用SRPV指令临时地更改Flash的Dummy Cycle?从灵活性角度,痞子衡推荐第二种方式,那么在FDCB里应该怎么做?痞子衡直接给答案:

// 设置Dummy Cycle数 #define FLASH_DUMMY_CYCLES 9 // 写Read Register时序在LUT中的index(可自定义位置,但不要占BootROM预设的几个时序位置) #define CMD_LUT_SEQ_IDX_SET_READ_PARAM 7 // BootROM中预设的LUT命令时序的index #define CMD_LUT_SEQ_IDX_READ 0 #define CMD_LUT_SEQ_IDX_READSTATUS 1 #define CMD_LUT_SEQ_IDX_WRITEENABLE 3 const flexspi_nor_config_t qspiflash_config = { .memConfig = { .tag = FLEXSPI_CFG_BLK_TAG, .version = FLEXSPI_CFG_BLK_VERSION, .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad, .csHoldTime = 3u, .csSetupTime = 3u, // Enable Safe configuration .controllerMiscOption = 0x10, .deviceType = kFlexSpiDeviceType_SerialNOR, .sflashPadType = kSerialFlash_4Pads, .serialClkFreq = kFlexSpiSerialClk_133MHz, .sflashA1Size = 16u * 1024u * 1024u, // 使能Flash寄存器配置操作 .configCmdEnable = 1u, .configModeType[0] = kDeviceConfigCmdType_Generic, // 指示Flash寄存器配置时序在LUT中index .configCmdSeqs[0] = { .seqNum = 1, .seqId = CMD_LUT_SEQ_IDX_SET_READ_PARAM, .reserved = 0, }, // 设定Flash寄存器配置值(这里就是写入Read Register的值) .configCmdArgs[0] = FLASH_DUMMY_CYCLES << 3, .lookupTable = { // Fast Read Quad I/O [4*CMD_LUT_SEQ_IDX_READ] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), [4*CMD_LUT_SEQ_IDX_READ + 1] = FLEXSPI_LUT_SEQ(MODE8_SDR, FLEXSPI_4PAD, 0x00, DUMMY_SDR, FLEXSPI_4PAD, FLASH_DUMMY_CYCLES-2), [4*CMD_LUT_SEQ_IDX_READ + 2] = FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_4PAD, 0x04, STOP, FLEXSPI_1PAD, 0x00), // READ STATUS REGISTER [4*CMD_LUT_SEQ_IDX_READSTATUS] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x01), [4*CMD_LUT_SEQ_IDX_READSTATUS + 1] = FLEXSPI_LUT_SEQ(STOP, FLEXSPI_1PAD, 0x00, 0, 0, 0), // WRTIE ENABLE [4*CMD_LUT_SEQ_IDX_WRITEENABLE] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, FLEXSPI_1PAD, 0x00), // Flash寄存器配置时序(这个时序需要上面READ STATUS, WRITE ENABLE的配合) [4*CMD_LUT_SEQ_IDX_SET_READ_PARAM] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x63, WRITE_SDR, FLEXSPI_1PAD, 0x01), [4*CMD_LUT_SEQ_IDX_SET_READ_PARAM + 1] = FLEXSPI_LUT_SEQ(STOP, FLEXSPI_1PAD, 0x00, 0, 0, 0), }, }, .pageSize = 256u, .sectorSize = 4u * 1024u, .blockSize = 256u * 1024u, .isUniformBlockSize = false, }; 四、BootROM对FDCB里Flash寄存器配置的处理

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsjpj.html