痞子衡嵌入式:在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是不是可以任意配呢?答案既是也不是,痞子衡先卖个关子。
让我们再来回顾下这颗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里应该怎么做?痞子衡直接给答案:
// 设置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寄存器配置的处理
说到BootROM对FDCB的处理,大家有必要先回顾下痞子衡写过的一篇旧文 《深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程》,文章虽然是针对i.MXRT1050写的,但基本流程也差不多适用i.MXRT1170,在文中的 2.6 小节第二次初始化里,我们在上面FDCB里的关于Flash寄存器的额外配置操作才会被处理,代码大致如下:
status_t flexspi_init(uint32_t instance, flexspi_mem_config_t *config)
{
status_t status = kStatus_InvalidArgument;
// 决定是否用30MHz SDR的安全时钟来做FlexSPI初始化
bool need_safe_freq = (config->deviceModeCfgEnable || config->configCmdEnable) &&
((config->controllerMiscOption & (1 << kFlexSpiMiscOffset_SafeConfigFreqEnable)));
if (need_safe_freq)
{
flexspi_clock_config(instance, kFlexSpiSerialClk_SafeFreq, kFlexSpiClk_SDR);
}
else
{
flexspi_clock_config(instance, config->serialClkFreq, flexspi_is_ddr_mode_enable(config));
}
// 省略代码片段,包含FlexSPI引脚、模块本身等各种初始化
// 实现Flash配置寄存器写入操作
if (config->configCmdEnable)
{
// PortA1/A2/B1/B2如使能则全写一遍
flexspi_device_cmd_config_all_chips(instance, config);
}
if (need_safe_freq)
{
// 重新配回指定的FlexSPI时钟
flexspi_clock_config(instance, config->serialClkFreq, flexspi_is_ddr_mode_enable(config));
}
return status;
}
void flexspi_device_cmd_config_all_chips(uint32_t instance, flexspi_mem_config_t *config)
{
uint32_t baseAddr = 0;
uint32_t *flashSizeStart = &config->sflashA1Size;
for (uint32_t index = 0; index < 4; index++)
{
uint32_t currentFlashSize = *flashSizeStart++;
if (currentFlashSize > 0)
{
flexspi_device_cmd_config(instance, config, baseAddr);
baseAddr += currentFlashSize;
}
}
}
void flexspi_device_cmd_config(uint32_t instance, flexspi_mem_config_t *config, uint32_t baseAddr)
{
FLEXSPI_Type *base = flexspi_get_module_base(instance);
flexspi_xfer_t flashXfer;
flashXfer.operation = kFlexSpiOperation_Config;
flashXfer.baseAddress = baseAddr;
flashXfer.isParallelModeEnable = false;
flashXfer.txSize = 4;
for (uint32_t index = 0; index < 3; index++)
{
if (config->configCmdSeqs[index].seqId > 0)
{
// If device is working under DPI/QPI/OPI mode, ignore SPI2XPI command
uint32_t read_cmd_pads = (base->LUT[0] >> 8) & 0x03;
if ((read_cmd_pads > FLEXSPI_1PAD) && (config->configModeType[index] == kDeviceConfigCmdType_Spi2Xpi))
{
continue;
}
flashXfer.seqId = config->configCmdSeqs[index].seqId;
flashXfer.seqNum = config->configCmdSeqs[index].seqNum;
flashXfer.txBuffer = &config->configCmdArgs[index];
// 这里需要调用WRITE ENABLE指令
flexspi_device_write_enable(instance, config, false, baseAddr);
flexspi_update_lut(instance, 1, &config->lookupTable[4 * flashXfer.seqId], flashXfer.seqNum);
flashXfer.seqId = 1;
flexspi_command_xfer(instance, &flashXfer);
if ((!config->waitTimeCfgCommands) &&
(config->configModeType[index] != (uint8_t)kDeviceConfigCmdType_Spi2Xpi) &&
(config->configModeType[index] != (uint8_t)kDeviceConfigCmdType_Xpi2Spi))
{
// 这里需要调用READ STATUS指令
flexspi_device_wait_busy(instance, config, false, baseAddr);
}
else
{
flexspi_sw_delay_us(config->waitTimeCfgCommands * 100UL);
}
}
}
}
至此,Flash工作频率与Dummy Cycle的联系痞子衡便介绍完毕了,掌声在哪里~~~
欢迎订阅
文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。
痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle的更多相关文章
- 痞子衡嵌入式:从头开始认识i.MXRT启动头FDCB里的lookupTable
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT启动头FDCB里的lookupTable. 一个MCU内部通常有很多外设模块,这些外设模块是各MCU厂商做差异化产品的本质, ...
- 痞子衡嵌入式:在i.MXRT启动头FDCB里使能串行NOR Flash的DTR模式
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是在FDCB里使能串行NOR Flash的DTR模式. 前两篇文章 <IS25WP系列Dummy Cycle设置> 与 < ...
- 痞子衡嵌入式:在i.MXRT启动头FDCB里使能串行NOR Flash的Continuous read模式
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是在FDCB里使能串行NOR Flash的Continuous read模式. 前面关于串行Flash传输时序的文章 <Fast R ...
- 痞子衡嵌入式:在i.MXRT启动头FDCB里使能串行NOR Flash的QPI/OPI模式
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是在FDCB里使能串行NOR Flash的QPI/OPI模式. 我们知道 Flash 读时序里有五大子序列 CMD + ADDR + MO ...
- 痞子衡嵌入式:i.MXRT中FlexSPI外设对AHB Burst Read特性的支持
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是FlexSPI外设对AHB Burst Read特性的支持. 痞子衡之前写过一篇关于FlexSPI LUT的文章 <从头开始认识i ...
- 痞子衡嵌入式:在串口波特率识别实例里逐步展示i.MXRT上提升代码执行性能的十八般武艺
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是在串口波特率识别实例里逐步展示i.MXRT上提升代码执行性能的十八般武艺. 恩智浦 MCU SE 团队近期一直在加班加点赶 SBL 项目 ...
- 痞子衡嵌入式:i.MXRT中不支持DQS的FlexSPI引脚组连接Flash下载与启动注意事项
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT中不支持DQS的FlexSPI引脚组连接Flash下载与启动注意事项. 最近痞子衡在支持一个印度客户,这个客户项目主芯片选择 ...
- 痞子衡嵌入式:i.MXRT连接特殊Octal Flash时(OPI DTR模式下反转字节序)下载与启动注意事项(以MX25UM51245为例)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是OPI DTR模式下反转字节序的Octal Flash在i.MXRT下载与启动注意事项. 在恩智浦官方参考设计板 MIMXRT595-E ...
- 痞子衡嵌入式:揭秘i.MXRT1170上串行NOR Flash双程序可交替启动设计
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1170上串行NOR Flash双程序可交替启动设计. 在上一篇文章 <i.MXRT1060/1010上串行NOR F ...
随机推荐
- uniapp 发起网络请求
推荐下我写的uni-http 创建http-config.js import Vue from 'vue' const BASE_URL = 'http://xxx.com'; if (process ...
- 05.其他创建numpy数组的方法
>>> import numpy as np >>> np.zeros(10,dtype=int) array([0, 0, 0, 0, 0, 0, 0, 0, 0 ...
- Warning: Cannot update during an existing state transition (such as within `render`). Render 报错
原来 修改(不用在构造函数里面定义)
- Gradle 差异化构建
Compile 默认的依赖方式,任何情况下都会依赖. Provided 只提供编译时依赖,打包时不会添加进去. Apk 只在打包Apk包时依赖,这个应该是比较少用到的. TestCompile 只在测 ...
- 后端程序员之路 57、go json
go自带json处理库,位于encoding/json,里面的test很具参考意义,特别是example_test.go json - The Go Programming Languagehttps ...
- 大话Spark(7)-源码之Master主备切换
Master作为Spark Standalone模式中的核心,如果Master出现异常,则整个集群的运行情况和资源都无法进行管理,整个集群将处于无法工作的状态. Spark在设计的时候考虑到了这种情况 ...
- selenium之元素定位的方法(二)
XPath定位是XML Path的缩写,称为XML路径语言,是在XML文档中查找信息的一种语言,可用来再XML文档中对元素和属性进行搜索.XPath使用路径表达式来选取XML文档中的节点或节点集. X ...
- MySQL使用入门--初识数据库
MySQL使用入门 数据库概述 数据库是存放数据的仓库.在应用的开发中总是离不开数据的查询.处理.存储,例如图书管理系统就需要操纵和存储大量的数据.没有数据库之前我们使用文件存储数据,但是文件存储有很 ...
- MyBatis中模糊查询
接口 // 模糊查询 List<User> getUserLike(String value); Mapper.xml文件 <!-- 模糊查询 --> <select i ...
- Linux下找出吃内存的方法总结
Linux下查询进程占用的内存方法总结,假设现在有一个「php-cgi」的进程 ,进程id为「25282」. 现在想要查询该进程占用的内存大小.linux命令行下有很多的工具进行查看,现总结常见的几种 ...