请选择 进入手机版 | 继续访问电脑版
查看: 1378|回复: 3

[分享] 使能串行NOR Flash基础读模式的一个小误区

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

    [LV.8]以坛为家I

    3298

    主题

    6545

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32001
    最后登录
    2024-4-9
    发表于 2021-6-4 12:32:17 | 显示全部楼层 |阅读模式
    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT中FlexSPI外设lookupTable里配置Normal read的一个小误区。


    关于串行四线NOR Flash,当其作为启动(XiP)设备时,我们最常配置的读模式应该是 Fast Read Quad I/O SDR (0xEB),这种模式在数据传输时会用上全部四根I/O线(IO0-3),并且SCK可达最高频率(通常133MHz),这种读模式下Flash性能相当高。但有时候某些设计里为了保证通用性(比如我们想要一个兼容所有类型Flash型号的启动头),我们也会尝试配置最基础的读模式 Normal Read (0x03),基础的读模式在数据传输时仅使用一根I/O线(IO1),并且SCK频率通常最高50MHz,这种模式其实更多是为了兼容SPI接口的EEPROM器件。


    Normal Read是任何串行NOR Flash都支持的读模式,也是最简单的一种模式,但在i.MXRT的FlexSPI外设里配置这种模式会存在关于Dummy Cycle设置的一个小误区,且听痞子衡道来:


    一、在FDCB里使能Normal Read
    关于FDCB及lookupTable相关知识详见痞子衡旧文 《从头开始认识i.MXRT启动头FDCB里的lookupTable》。现在我们尝试准备一个使能Normal read的FDCB头,Flash器件就以华邦W25Q64JWS-IQ为例,查看其数据手册,找到如下Normal read时序图:
    1.png
    从Normal read时序图里可以看出,其仅包含命令序列、地址序列、读数据序列、停止序列共四个子序列,与Fast Read Quad I/O SDR时序相比少了模式序列和Dummy序列,因此示例FDCB如下。经i.MXRT1050-EVKB板子实测,这个示例FDCB是可以正常用于启动的。
    1. const flexspi_nor_config_t qspiflash_config = {
    2.     .memConfig =
    3.         {
    4.             .tag              = FLEXSPI_CFG_BLK_TAG,
    5.             .version          = FLEXSPI_CFG_BLK_VERSION,
    6.             // 低速情况下可以使用LoopbackInternally
    7.             .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally,
    8.             .csHoldTime       = 3u,
    9.             .csSetupTime      = 3u,
    10.             .controllerMiscOption = 0x10,
    11.             .deviceType       = kFlexSpiDeviceType_SerialNOR,
    12.             // 实际上这里不管设置1Pad/2Pads/4Pads,在iomuxc里都会配置IO0-3
    13.             .sflashPadType    = kSerialFlash_1Pad,
    14.             // 配置SCK频率不要超过50MHz
    15.             .serialClkFreq    = kFlexSpiSerialClk_50MHz,
    16.             .sflashA1Size     = 8u * 1024u * 1024u,
    17.             .lookupTable =
    18.                 {
    19.                     // Normal Read LUTs
    20.                     // 包含四个子序列,且全是通过 FLEXSPI_1PAD 传输
    21.                     [4*CMD_LUT_SEQ_IDX_READ + 0] = FLEXSPI_LUT_SEQ(CMD_SDR,   FLEXSPI_1PAD, 0x03, RADDR_SDR, FLEXSPI_1PAD, 0x18),
    22.                     [4*CMD_LUT_SEQ_IDX_READ + 1] = FLEXSPI_LUT_SEQ(READ_SDR,  FLEXSPI_1PAD, 0x04, STOP,      FLEXSPI_1PAD, 0x00),
    23.                 },
    24.         },
    25.     .pageSize           = 256u,
    26.     .sectorSize         = 4u * 1024u,
    27.     .blockSize          = 64u * 1024u,
    28.     .isUniformBlockSize = false,
    29. };
    复制代码
    按照文章 《实抓Flash信号波形来看i.MXRT的FlexSPI外设下AHB读访问情形(无缓存)》 第二小节里的软硬件测试环境,我们测试无缓存下的 memcpy((void *)0x20200000, (void *)0x60002400, 8); 语句执行在Flash端时序如下(为便于捕捉Flash信号,实际测试时SCK频率降到了30MHz):
    2.png
    二、关于Dummy Cycle的小误区是什么?
    在配置Fast Read Quad I/O SDR时序的lookupTable里,我们常常会根据不同的Flash器件特性在Dummy子序列里填入不同的Cycle数值。现在我们配置的是Normal read时序,有人可能会保留Dummy子序列,但是将其参数值设0(如下代码所示),根据字面理解,这样似乎也没问题,但在FlexSPI外设里,这样是不行的,这就是关于Dummy Cycle的误区。
    1. #include "fsl_flexspi.h"

    2. #define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL 0

    3. uint32_t s_customLUT_wrong[4] = {
    4.     /* Normal read mode -SDR */
    5.     [4*NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 0] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,       kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    6.     // 保留kFLEXSPI_Command_DUMMY_SDR序列,但参数值填0
    7.     [4*NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x00, kFLEXSPI_Command_READ_SDR,  kFLEXSPI_1PAD, 0x04),
    8.     [4*NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 2] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_STOP,      kFLEXSPI_1PAD, 0x00, 0, 0, 0),
    9. };
    复制代码
    三、Dummy Cycle设0的误区带来什么后果?
    使用第二小节里的 s_customLUT_wrong[] 去配置FlexSPI LUT到底会产生什么后果,我们继续在文章 《实抓Flash信号波形来看i.MXRT的FlexSPI外设下AHB读访问情形(无缓存)》 第二小节里的软硬件测试环境下抓一下时序图。可先借助SDK里flexspi驱动如下两个函数来更新LUT,因为是在XiP环境下执行的ramfunc代码,所以无需再从头初始化FlexSPI模块。
    1. #include "fsl_flexspi.h"

    2. /* Update LUT table. */
    3. FLEXSPI_UpdateLUT(FLEXSPI, 0, s_customLUT_wrong, 4);

    4. /* Do software reset. */
    5. FLEXSPI_SoftwareReset(FLEXSPI);
    复制代码
    现在我们在IAR下在线调试看看结果,当使用 s_customLUT_wrong 更新掉FlexSPI的LUT后,Flash的访问立刻出现了异常,memory窗口观察到的数值全部变成了0xFF, 并且拷贝目的地内部RAM相应地址处也全是0xFF,说明从功能角度 memcpy 语句并没有产生预想效果(但此时 ramfunc 代码是能正常往下执行的)。
    3.png
    抓出Flash端波形,我们发现仅8字节数据的 memcpy 竟然产生了长达 33ms 的读时序(换算一下,即实际读取了约128KB的数据),放大最前面的时序,可以看到命令(0x03)、地址(0x002400)、初始读出数据(0x01、0x02、0x03...)都是正常的,并且确实没有Dummy Cycle子序列,对比应用程序binary文件后发现还真的按序读出了128KB有效Flash数据,所以Flash端本身的时序响应没有问题,但AHB总线与FlexSPI外设之间的配合出了问题,导致CPU无法拿到有效Flash数据,这也是开发环境memory窗口无法再看到真实Flash数据的原因。至于为何读到128KB数据才停下来,痞子衡也不得而知。如果代码里循环执行这句 memcpy 语句,Flash端就也相应循环出现 33ms 的读时序。
    4.png
    至此,i.MXRT中FlexSPI外设lookupTable里配置Normal read的一个小误区痞子衡便介绍完毕了,掌声在哪里~~~


    附录、MIMXRT1050下完整FlexSPI初始化代码
    如果上述异常Normal read时序测试不是在XiP环境下进行的,那么就需要完整地初始化FlexSPI外设,可以使用如下 flexspi_nor_flash_init() 函数:
    1. #include "clock_config.h"
    2. #include "fsl_flexspi.h"

    3. flexspi_device_config_t s_deviceconfig = {
    4.     .flexspiRootClk       = 30000000,
    5.     .flashSize            = 0x2000,  /* 64Mb/KByte */
    6.     .CSIntervalUnit       = kFLEXSPI_CsIntervalUnit1SckCycle,
    7.     .CSInterval           = 2,
    8.     .CSHoldTime           = 3,
    9.     .CSSetupTime          = 3,
    10.     .dataValidTime        = 0,
    11.     .columnspace          = 0,
    12.     .enableWordAddress    = 0,
    13.     .AWRSeqIndex          = 0,
    14.     .AWRSeqNumber         = 0,
    15.     .ARDSeqIndex          = NOR_CMD_LUT_SEQ_IDX_READ_NORMAL,
    16.     .ARDSeqNumber         = 1,
    17.     .AHBWriteWaitUnit     = kFLEXSPI_AhbWriteWaitUnit2AhbCycle,
    18.     .AHBWriteWaitInterval = 0,
    19. };

    20. static inline void flexspi_clock_init(void)
    21. {
    22.     const clock_usb_pll_config_t g_ccmConfigUsbPll = {.loopDivider = 0U};

    23.     CLOCK_InitUsb1Pll(&g_ccmConfigUsbPll);
    24.     CLOCK_InitUsb1Pfd(kCLOCK_Pfd0, 35);   /* Set PLL3 PFD0 clock 247MHZ. */
    25.     CLOCK_SetMux(kCLOCK_FlexspiMux, 0x3); /* Choose PLL3 PFD0 clock as flexspi source clock. */
    26.     CLOCK_SetDiv(kCLOCK_FlexspiDiv, 7);   /* flexspi clock 30M. */
    27. }

    28. void flexspi_nor_flash_init(void)
    29. {
    30.     flexspi_clock_init();

    31.     /* Get FLEXSPI default settings and configure the flexspi. */
    32.     flexspi_config_t config;
    33.     FLEXSPI_GetDefaultConfig(&config);

    34.     /* Set AHB buffer size for reading data through AHB bus. */
    35.     config.ahbConfig.enableAHBPrefetch    = false;
    36.     config.ahbConfig.enableAHBBufferable  = true;
    37.     config.ahbConfig.enableReadAddressOpt = true;
    38.     config.ahbConfig.enableAHBCachable    = false;
    39.     config.rxSampleClock                  = kFLEXSPI_ReadSampleClkLoopbackInternally;
    40.     FLEXSPI_Init(FLEXSPI, &config);

    41.     /* Configure flash settings according to serial flash feature. */
    42.     FLEXSPI_SetFlashConfig(FLEXSPI, &s_deviceconfig, kFLEXSPI_PortA1);

    43.     /* Update LUT table. */
    44.     FLEXSPI_UpdateLUT(FLEXSPI, 0, s_customLUT_wrong, 4);

    45.     /* Do software reset. */
    46.     FLEXSPI_SoftwareReset(FLEXSPI);
    47. }
    复制代码




    签到签到
    回复

    使用道具 举报

  • TA的每日心情
    开心
    16 小时前
  • 签到天数: 1288 天

    [LV.10]以坛为家III

    7

    主题

    6171

    帖子

    1

    金牌会员

    Rank: 6Rank: 6

    积分
    10363
    最后登录
    2024-4-18
    发表于 2021-6-8 18:17:51 | 显示全部楼层
    感谢分享!!
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2023-8-30 17:43
  • 签到天数: 306 天

    [LV.8]以坛为家I

    14

    主题

    1746

    帖子

    4

    金牌会员

    Rank: 6Rank: 6

    积分
    2525
    最后登录
    2023-8-30
    发表于 2021-6-8 18:34:50 | 显示全部楼层
    感谢分享
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情

    2024-2-5 12:06
  • 签到天数: 627 天

    [LV.9]以坛为家II

    94

    主题

    1628

    帖子

    2

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    4429

    热心会员

    最后登录
    2024-2-5
    发表于 2021-6-8 19:09:22 | 显示全部楼层
    文章讲的非诚好。在配置工具里面就有dummy时间的配置,默认的就是0。看了后面也不能完全用默认值了。
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-18 16:56 , Processed in 0.129548 second(s), 25 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

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