大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是在FDCB里使能串行NOR Flash的DTR模式。
前两篇文章 《IS25WP系列Dummy Cycle设置》 与 《IS25LP系列Dummy Cycle设置》, 痞子衡均是设置Flash的Fast Read Quad I/O SDR模式去启动的,但最近在恩智浦官方论坛上,有不止一个客户需要使能Flash的DTR模式去启动,他们似乎都遇到了小问题,难道DTR模式藏着什么玄机?走,跟痞子衡去瞧瞧:
一、什么是DTR模式?
DTR是Dual Transfer Rate的缩写,即在时钟信号SCK的双边沿均触发数据传输。DTR有时候跟另一个名词DDR会混用,DDR是Double Data Rate的缩写,反正这两个名词均表示双边沿触发的意思,跟SDR单边沿触发相比快了一倍(同等SCK频率下而言)。下面痞子衡结合i.MXRT的FlexSPI外设来对比介绍SDR模块与DDR模式的区别:
1.1 FlexSPI的DTR输入输出
下图是FlexSPI的输入时序图(FlexSPI从Flash读取存储数据)。左边是SDR模式,可以看到FlexSPI是在DQS的下沿才会去锁存一次SIO上的数据。右边是DDR模式,FlexSPI外设在DQS的上沿和下沿都会去锁存一次SIO上的数据。注意不管是SDR模块还是DDR模式,DQS信号都是与SCK同频的。
Note: 关于DQS信号的意义,可以去看痞子衡的旧文 《串行NOR Flash的DQS信号功能简介》,FlexSPI外设的DQS信号既可以来自真实的Flash器件输出,也可以从i.MXRT的DQS引脚loopback(回环)。
再来看FlexSPI的输出时序图(FlexSPI给Flash发送地址,模式等)。左边是SDR模式,Flash器件应在SCK的上沿去锁存DATA (SIO)上的数据。右边是DDR模式,Flash器件需要在SCK的上沿和下沿都去锁存DATA (SIO)上的数据。
1.2 Fast Read Quad I/O DTR时序
了解了SDR与DDR模式区别,我们再来看LUT里Quad I/O Read DDR传输序列,它由CMD_SDR + RADDR_DDR + MODE8_DDR + DUMMY_DDR + LEARN_DDR(可选) + READ_DDR + STOP七个子序列组成,如下表所示。
Note: 特别注意,虽然存在CMD_DDR子序列,但Quad I/O Read DDR传输下发送命令规定使用CMD_SDR子序列。
从引脚信号上来看,完整Quad I/O Read DDR传输时序如下图所示。下图是以两片四线S25FS512S组parallel mode(八线)来示例的。如果是常见的individual mode,我们仅关注PCSA1相关的信号时序即可。
1.3 DTR下实际Dummy Cycle输出与LUT中设定值关系
从Flash器件端来看,实际Dummy Cycle输出数是跟SCK信号周期数一一对应的。在SDR模式下,在Dummy Cycle序列时间内,有多少个SCK周期,即是有多少个Dummy Cycle数,填进LUT的Dummy Cycle也是这个值,这个没有疑义。那么在DDR模式,填入LUT的Dummy Cycle值与SCK周期数是什么关系呢?翻看FlexSPI章节,在LUT指令集表格里有答案,此时Dummy Cycle值应该是实际SCK周期数的2倍(可能有+/-1,需要看Flash手册),这是因为LUT中Dummy Cycle值是与FlexSPI外设端的Serial Root Clk数一一对应的:在SDR模式下,Serial Root Clk周期等于SCK周期;而在DDR模式下,Serial Root Clk周期只有SCK周期的一半。
1.4 DTR下支持的最高SCK频率
上面在讲Dummy Cycle值的时候提到了Serial Root Clk概念,这个其实就是FlexSPI模块本身的工作时钟,目前已量产的i.MXRT型号(包括i.MXRT1010/1020/1050/1060/1170),其FLEXSPI_CLK_ROOT最高频率均是332MHz(下表来自i.MXRT1050系列手册)。
在332MHz FLEXSPI_CLK_ROOT频率下,DTR模式SCK频率理论上最高可以到166MHz,这个没有疑义。但是SDR模式SCK频率理论上最高也是166MHz,因为SDR模式下,只保证了在166MHz FLEXSPI_CLK_ROOT频率下时序可靠(别问,问就是FlexSPI就是这么设计的)。
具体SCK能配到多高的频率跟FlexSPI模块一个寄存器的配置值有关,即FlexSPI->MCR0[RXCLKSRC],RXCLKSRC配置的是DQS信号的来源,DQS信号一共有三种来源:无DQS自回环(支持的SCK频率最低),有DQS自回环(支持的SCK频率升高),来自外部Flash器件的DQS引脚(支持的SCK频率最高)。
那么RXCLKSRC三种配置下,SCK分别能达到多高频率呢?这需要查看i.MXRT芯片的datasheet,在 FlexSPI input/read timing 小节里有详细介绍,痞子衡整理如下:
Flash工作模式 | RXCLKSRC = 0 | RXCLKSRC = 1 | RXCLKSRC = 3 |
---|---|---|---|
SDR | SCK最高60MHz | SCK最高133MHz | SCK最高166MHz |
DDR | SCK最高30MHz | SCK最高66MHz | SCK最高166MHz |
二、在客户问题中实战
了解了上面DTR模式基础知识后,我们去恩智浦官方论坛找两个相关问题实践一下。
2.1 Flash型号IS25LP064A
先来看第一个问题 《i.MX RT1021 + IS25LP064A XIP flash in DDR mode settings》,这个客户使用的Flash型号是IS25LP064A,客户已经修改了FDCB头,但是他犯了一个比较严重的错误,他使用了CMD_DDR来发送命令而不是CMD_SDR,这显然不符合Flash时序规范。
此外,该款Flash在DTR模式下最高能支持到66MHz,客户在FDCB里配置SCK频率为 kFlexSpiSerialClk_60MHz 是没问题的,这个速度下DTR模式需要4个SCK周期做Dummy Cycle,Flash器件里的默认值3按说不符合要求,但实测启动也没问题。
下面痞子衡给一个适用的FDCB头(50MHz SCK,DTR模式,LUT里等效Dummy Cycle值是0x2 + 0x4,即6个Dummy Cycle)。想同步修改Flash器件里的Dummy Cycle值去规范地调高SCK频率到60MHz,请参考 《IS25LP系列Dummy Cycle设置》。
const flexspi_nor_config_t qspiflash_config = {
.memConfig =
{
.tag = FLEXSPI_CFG_BLK_TAG,
.version = FLEXSPI_CFG_BLK_VERSION,
// 设置 FlexSPI->MCR0[RXCLKSRC] 为 1
.readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad,
.csHoldTime = 3u,
.csSetupTime = 3u,
// 使能 DDR 模式
.controllerMiscOption = kFlexSpiMiscOffset_DdrModeEnable | kFlexSpiMiscOffset_SafeConfigFreqEnable,
.sflashPadType = kSerialFlash_4Pads,
// 设置 DDR 模式下工作频率
.serialClkFreq = kFlexSpiSerialClk_50MHz,
.sflashA1Size = 8u * 1024u * 1024u,
.lookupTable =
{
// Read LUTs
[4*CMD_LUT_SEQ_IDX_READ] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xED, RADDR_DDR, FLEXSPI_4PAD, 0x18),
// MODE8_DDR子序列等效2个cycle,DUMMY_DDR子序列里还需设置4个cycle,总计3个SCK周期
[4*CMD_LUT_SEQ_IDX_READ + 1] = FLEXSPI_LUT_SEQ(MODE8_DDR, FLEXSPI_4PAD, 0x00, DUMMY_DDR, FLEXSPI_4PAD, 0x04),
[4*CMD_LUT_SEQ_IDX_READ + 2] = FLEXSPI_LUT_SEQ(READ_DDR, FLEXSPI_4PAD, 0x04, STOP, FLEXSPI_1PAD, 0x00),
},
},
.pageSize = 256u,
.sectorSize = 4u * 1024u,
.blockSize = 64u * 1024u,
.isUniformBlockSize = false,
};
2.2 Flash型号IS25WP064D
再来看第二个问题 《i.MX RT106x + IS25WP064D QSPI DDR mode》,这个客户使用的Flash型号是IS25WP064D,客户也修改好了FDCB头,由于Flash器件本身最高能支持80MHz DTR模式,于是这个客户就想在FDCB里将SCK频率设为 kFlexSpiSerialClk_80MHz,但是Flash本身没有DQS信号,FlexSPI端仅能从DQS引脚loopback,这种情况下FlexSPI最高仅能支持66MHz DTR,这显然不符合FlexSPI规范。
痞子衡特别注意到,这款IS25WP064D与上一个客户用的IS25LP064A在时序上尤其是Dummy Cycle上有明显的区别。同样SCK频率下,IS25LP064A下SDR与DDR模式的Dummy Cycle是两倍关系,但是IS25WP064D下SDR与DDR模式的Dummy Cycle数是一样的。
IS25WP064D默认6个Dummy Cycle下对应最高DTR工作频率是69MHz,这已经达FlexSPI外设最高支持的DTR频率上限了,因此不需更改Flash里的Dummy Cycle设置了。
下面痞子衡给一个适用的FDCB头(60MHz SCK,DTR模式,LUT里等效Dummy Cycle值是0x2 + 0xa,即12个Dummy Cycle)。
const flexspi_nor_config_t qspiflash_config = {
.memConfig =
{
.tag = FLEXSPI_CFG_BLK_TAG,
.version = FLEXSPI_CFG_BLK_VERSION,
// 设置 FlexSPI->MCR0[RXCLKSRC] 为 1
.readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad,
.csHoldTime = 3u,
.csSetupTime = 3u,
// 使能 DDR 模式
.controllerMiscOption = kFlexSpiMiscOffset_DdrModeEnable | kFlexSpiMiscOffset_SafeConfigFreqEnable,
.sflashPadType = kSerialFlash_4Pads,
// 设置 DDR 模式下工作频率
.serialClkFreq = kFlexSpiSerialClk_60MHz,
.sflashA1Size = 8u * 1024u * 1024u,
.lookupTable =
{
// Read LUTs
[4*CMD_LUT_SEQ_IDX_READ] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xED, RADDR_DDR, FLEXSPI_4PAD, 0x18),
// MODE8_DDR子序列等效2个cycle,DUMMY_DDR子序列里还需设置10个cycle,总计6个SCK周期
[4*CMD_LUT_SEQ_IDX_READ + 1] = FLEXSPI_LUT_SEQ(MODE8_DDR, FLEXSPI_4PAD, 0x00, DUMMY_DDR, FLEXSPI_4PAD, 0x0a),
[4*CMD_LUT_SEQ_IDX_READ + 2] = FLEXSPI_LUT_SEQ(READ_DDR, FLEXSPI_4PAD, 0x04, STOP, FLEXSPI_1PAD, 0x00),
},
},
.pageSize = 256u,
.sectorSize = 4u * 1024u,
.blockSize = 64u * 1024u,
.isUniformBlockSize = false,
};
至此,在FDCB里使能串行NOR Flash的DTR模式痞子衡便介绍完毕了,掌声在哪里~~~