大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT启动头FDCB里的lookupTable

  一个MCU内部通常有很多外设模块,这些外设模块是各MCU厂商做差异化产品的本质,也是各厂商核心竞争力所在(这里特指那些生产ARM Cortex-M内核MCU的厂商)。在做MCU开发时有时候并不需要了解全部的外设,因为有些外设在项目里不一定会用到,但是要想把恩智浦i.MXRT系列MCU玩起来,有一个外设是必须要有所了解的,它就是FlexSPI,这个外设负责与外部串行NOR Flash连接,实现外部NOR Flash里的应用程序指令与数据的读取,而串行NOR Flash正是i.MXRT首选的启动设备。

  那么在FlexSPI外设模块里究竟是什么机制实现了Flash中应用程序指令与数据的读取功能呢?痞子衡从i.MXRT启动头FDCB里的lookupTable设定开始说起:

一、为何i.MXRT能从外部Flash XIP启动?

  关于在串行NOR Flash XIP执行原理,痞子衡其实在之前一篇文章 《在串行NOR Flash XIP调试原理》 的第二小节 i.MXRT FlexSPI外设特性 介绍过,是FlexSPI这个外设实现了从串行Flash任意地址取指令的功能,这是先决条件。

  有了从Flash任意地址取指的先决条件基础,在i.MXRT芯片上电后,BootROM便只需要将FlexSPI外设配置到指定工作状态(这里详见 《深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程》 一文,尤其是文中最后一节提到的第二次FlexSPI初始化,本文讨论的内容其实属于第二次初始化后的状态),FlexSPI外设配置信息完全来自于启动头FDCB(一共512bytes),FlexSPI配置完成后,BootROM再把CPU控制权交给应用程序,这就完成了启动任务。

  下面的 qspiflash_config 便是i.MXRT SDK包里使用的一个典型的适用符合JEDEC SFDP标准且容量为8MB的QSPI NOR Flash的FDCB头。这个启动头将FlexSPI配置成了四线模式,100MHz时钟频率,Quad I/O Fast Read时序模式(注意这个头里lookupTable设定写法其实并不标准,没有显式地写出模式序列和停止序列,后面痞子衡会细说):

  当PC开始指向FlexSPI映射空间(0x60000000 - 0x607FFFFF)去执行用户程序时,FlexSPI便在背后一直默默为CPU送上指定的指令数据,如下图绿色箭头流向所示。指令数据从外部Flash中通过IO_CTL且按照SEQ_CTL指定的时序送入RX_FIFO,再到AHB_RX_BUF,最后经过AHB_CTL送到系统AHB总线上,以被CPU无障碍获取。整个过程中最重要的自动化环节其实是黄色框内的SEQ_CTL,是这个SEQ_CTL在时刻驱动着FlexSPI发送符合Flash要求的读访问时序。

二、FlexSPI外设的SEQ_CTL是如何工作的?

  经过上一节的分析,我们知道了是FlexSPI中的SEQ_CTL组件实现了核心的Flash访问时序控制,那么SEQ_CTL我们该怎么控制它?别急,这时候该LUT登场,LUT是Look Up Table的简称,它其实是FlexSPI内部的一块存储区(即FlexSPI->LUTx寄存器),它的组织结构如下,LUT由多个Sequence组成(比如i.MXRT1050上是16个),每个Sequence由最多8个instruction组成,每个instruction大小为16bits,分为opcode(序列编号) + num_pads(管脚模式) + operand(序列参数值)三部分。

  每个instruction,你可以理解为一个Flash访问传输子序列(比如命令序列、地址序列、模式序列,dummy序列,读/写数据序列,停止序列等),在FlexSPI外设模块里面预先实现了很多个基础instruction,instruction中的opcode即是那些预实现的序列编号。opcode全部编号如下:

命令序列:
CMD_SDR - 0x01, CMD_DDR - 0x21
地址序列:
RADDR_SDR - 0x02, RADDR_DDR - 0x22, CADDR_SDR - 0x03, CADDR_DDR - 0x23
模式序列:
MODE1_SDR - 0x04, MODE1_DDR - 0x24, MODE2_SDR - 0x05, MODE2_DDR - 0x25
MODE4_SDR - 0x06, MODE4_DDR - 0x26, MODE8_SDR - 0x07, MODE8_DDR - 0x27
写数据序列:
WRITE_SDR - 0x08, WRITE_DDR - 0x28
读数据序列:
READ_SDR - 0x09, READ_DDR - 0x29
LEARN序列:
LEARN_SDR - 0x0A, LEARN_DDR - 0x2A
数据长度设置序列(适用FPGA):
DATSZ_SDR - 0x0B, DATSZ_DDR - 0x2B
空指令序列::
DUMMY_SDR - 0x0C, DUMMY_DDR - 0x2C, DUMMY_RWDS_SDR - 0x0D, DUMMY_RWDS_DDR - 0x2D
JMP序列:
JMP_ON_CS - 0x1F
停止序列:
STOP - 0x00

  有了这些基础instruction,我们便可以自由组合它们(最多8个),得到我们想要的完整传输Sequence。比如最常见的Quad I/O Read SDR传输时序便由CMD_SDR + RADDR_SDR + MODE8_SDR + DUMMY_SDR + READ_SDR + STOP六个子序列组成,如下表所示:

  • Note: 关于READ_SDR的参数值设置(即读取数据长度)需要特别说明一下,这个参数仅对IP CMD方式的访问时序有效;而对于AHB CMD方式的访问时序,这个参数值设定是无效的,实际读取数据长度是由AHB RX Buffer策略灵活决定的。

  从引脚信号上来看,完整Quad I/O Read SDR传输时序如下图所示。注意有一处要特别说明,从FlexSPI外设本身而言,MODE8_SDR序列和DUMMY_SDR序列是互相独立的,但在不少Flash芯片上,MODE8_SDR所占的2个时钟周期也被算在了总Dummy时钟周期数里。

  LUT中最多可以存储16个Sequence,对于XIP执行而言,只需要一个读访问时序(比如最常用的Quad I/O Read SDR传输时序)即可。如果是IAP,那么还需要添加擦除时序,写访问时序,写使能时序,读状态寄存器时序等。这些预先存放在LUT中的Sequence被用户按需触发以实现各种不同类型的Flash访问,这就是SEQ_CTL工作机制。

三、FDCB中的lookupTable是如何配置进FlexSPI->LUT的?

  从FlexSPI外设模块设计上而言,LUT里16个Sequence地位是相同的,对于XIP执行,必要的读访问时序可以放在LUT中的任何一个Sequence位置,只需要在FlexSPI->FLSHxCR2寄存器(x可取A1/A2/B1/B2,具体根据Flash引脚连接来定)中的ARDSEQID位指明读访问时序在LUT中的位置(index)即可。

  但是毕竟应用程序是由BootROM引导的,BootROM有自己的一套配置FlexSPI规则,它定死了CMD_LUT_SEQ_IDX_READ位置,即读访问时序必须是FlexSPI->LUT[]中第一个Sequence,因为FlexSPI->FLSHxCR2[ARDSEQID]被BootROM配置成了0。所以我们在准备FDCB时,lookupTable中第一个Sequence必须放置读访问时序。

  再来看BootROM中的FlexSPI初始化函数,在外设模块基本初始化 flexspi_init() 完成后,然后 flexspi_update_lut() 被调用去更新了一次LUT就直接结束了。这次的LUT更新其实仅仅是将FDCB里的lookupTable[0] - lookupTable[3](第一条Sequence) 填到 FlexSPI->LUT[0] - FlexSPI->LUT[3]里。至于为何有时候你会看到FDCB里lookupTable中不止一条Sequence,这个痞子衡后面另有文章再聊。

status_t flexspi_nor_flash_init(uint32_t instance, flexspi_nor_config_t *config)
{
status_t status = kStatus_InvalidArgument; status = flexspi_init(instance, (flexspi_mem_config_t *)config);
if (status != kStatus_Success)
{
break;
} // Configure Lookup table for Read
// 将config->memConfig.lookupTable里的第一个sequence放到FlexSPI->LUT[0] - FlexSPI->LUT[3]里
flexspi_update_lut(instance, 0, config->memConfig.lookupTable, 1); return status;
}

四、设定FDCB中lookupTable的一个实例

  我们以i.MXRT官方EVK上配套的典型Flash型号IS25WP064AJBLE来实战,下图是该Flash的Fast Read Quad I/O Sequence,这个时序图中命令序列、地址序列、Dummy序列的参数值是明确的,但模式序列、读数据序列参数值并不明确,我们给它明确一下,模式序列中mode bits我们设为0x00(其实只要不是0xAx均可),即 non-continuous read mode;读数据序列中data out byte其实不可设(上面讲过AHB访问下是由RX Buffer策略自动控制的),随便写个非0值即可。

  基于上面的真实Flash读数据传输时序图,我们在FDCB中lookupTable里的对应设定应如下:

#define CMD_LUT_SEQ_IDX_READ        0

#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1)                                  \
(FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | \
FLEXSPI_LUT_OPERAND1(op1) | FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) #define FLEXSPI_1PAD 0
#define FLEXSPI_2PAD 1
#define FLEXSPI_4PAD 2
#define FLEXSPI_8PAD 3 const flexspi_nor_config_t qspiflash_config = {
.memConfig =
{
.lookupTable =
{
// Quad I/O Fast Read LUTs
// 第1个instruction是CMD_SDR,参数值为0xEB,即Quad I/O Fast Read命令
// 第2个instruction是RADDR_SDR,参数值为0x18,即24bits地址(三字节)
[4*CMD_LUT_SEQ_IDX_READ + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), // 第3个instruction是MODE8_SDR,参数值为0x00。注意对于IS25WP064AJBLE它同时也算2个Dummy时钟周期!!!
// 第4个instruction是DUMMY_SDR,参数值为0x04,加上上面一共6个时钟周期
[4*CMD_LUT_SEQ_IDX_READ + 1] = FLEXSPI_LUT_SEQ(MODE8_SDR, FLEXSPI_4PAD, 0x00, DUMMY_SDR, FLEXSPI_4PAD, 0x04), // 第5个instruction是READ_SDR,参数值为0x04,设定并不生效,随便写个非0值都行
// 第6个instruction是STOP
[4*CMD_LUT_SEQ_IDX_READ + 2] = FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_4PAD, 0x04, STOP, FLEXSPI_1PAD, 0x00),
[4*CMD_LUT_SEQ_IDX_READ + 3] = 0,
},
},
};

五、对FlexSPI映射区域进行AHB读访问一定会启动SEQ_CTL工作吗?

  当我们放好了正确的FDCB,BootROM正常配置完FlexSPI,并启动了应用程序后,CPU便开始按部就班从FlexSPI映射区域直接AHB访问去获取应用程序指令,是不是每一次的CPU访问都会让SEQ_CTL组件按LUT里的设定发送一次读访问时序呢?其实并不是!

  我们知道i.MXRT系列会有L1 Cache,如果Flash某地址里的指令内容缓存在L1 Cache里,那么当前CPU访问该Flash地址处的指令并不需要从Flash里重新再获取一次,CPU直接从cache里便可以得到指令,此时SEQ_CTL不会工作。

  即便L1 Cache里没有缓存到CPU所要指令,如果FlexSPI本身的Cacheable和Prefetch功能打开的话,AHB RX/TX Buffer里可能也会缓存CPU所要指令。如果所需指令确实缓存在AHB Buffer里,SEQ_CTL仍然不会工作。

  仅当CPU所要指令是全新的,完全没有缓存,SEQ_CTL才会真正开始工作,按LUT设定去发送读数据访问时序给Flash。

六、AHB读访问下SEQ_CTL工作一次到底获取多长的数据?

  前面讲了,我们在lookupTable里无法有效设置读数据序列中data out byte,因为AHB访问下的一次读取的长度是由RX Buffer策略控制的。在i.MXRT1050中AHB RX Buffer总大小为1KB,分为四个:AHB RX Buffer0 - AHB RX Buffer3,每个Buffer的大小都是可配的。具体配置在如下FlexSPI->AHBRXBUFxCR0寄存器里:

  BootROM使用了如下 flexspi_config_ahb_buffers() 函数配置了AHB Buffer,即开启了FlexSPI的Prefetch功能,并且将四个FlexSPI->AHBRXBUFxCR0[BUFSZ]全部设为了0,根据手册,这种配置意味着仅启用Buffer3作为唯一的RX Buffer,并且Buffer3大小为1KB。那么我们现在知道了,在Prefetch开启的情况下,SEQ_CTL工作一次就会读取1KB数据。当然Prefetch功能是可以在应用程序里被关掉的,如果Prefetch不使能,SEQ_CTL工作一次仅获取最小数据单元(8bytes)。

status_t flexspi_config_ahb_buffers(FLEXSPI_Type *base, flexspi_mem_config_t *config)
{
uint32_t temp;
uint32_t index;
status_t status = kStatus_InvalidArgument; do
{
if ((base == NULL) || (config == NULL))
{
break;
} if (config->deviceType == kFlexSpiDeviceType_SerialNOR)
{
// Configure AHBCR
temp = base->AHBCR & (~FLEXSPI_AHBCR_APAREN_MASK);
// Remove alignment limitation when Flash device works under DDR mode.
temp |= FLEXSPI_AHBCR_READADDROPT_MASK;
#if FLEXSPI_FEATURE_HAS_PARALLEL_MODE
if (flexspi_is_parallel_mode(config))
{
temp |= FLEXSPI_AHBCR_APAREN_MASK;
}
#endif // FLEXSPI_FEATURE_HAS_PARALLEL_MODE
base->AHBCR = temp;
} // Enable prefetch feature
base->AHBCR |= FLEXSPI_AHBCR_PREFETCHEN_MASK; // Skip AHB buffer configuration if corresponding bit is set
if ((config->controllerMiscOption & (1<<kFlexSpiMiscOffset_SkipAhbBufConfig)))
{
status = kStatus_Success;
break;
} // Configure AHB RX buffer
for (index = 0; index < FLEXSPI_AHBRXBUFCR0_COUNT - 1; index++)
{
base->AHBRXBUFCR0[index] &=
~(FLEXSPI_AHBRXBUFCR0_BUFSZ_MASK | FLEXSPI_AHBRXBUFCR0_MSTRID_MASK | FLEXSPI_AHBRXBUFCR0_PRIORITY_MASK);
}
status = kStatus_Success; } while (0); return status;
}

  至此,i.MXRT启动头FDCB里的lookupTable痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到我的 博客园主页CSDN主页知乎主页微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:从头开始认识i.MXRT启动头FDCB里的lookupTable的更多相关文章

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

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是Flash工作频率与Dummy Cycle的联系. 上一篇文章 <从头开始认识i.MXRT启动头FDCB里的lookupTable ...

  2. 痞子衡嵌入式:在i.MXRT启动头FDCB里使能串行NOR Flash的DTR模式

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是在FDCB里使能串行NOR Flash的DTR模式. 前两篇文章 <IS25WP系列Dummy Cycle设置> 与 < ...

  3. 痞子衡嵌入式:在i.MXRT启动头FDCB里使能串行NOR Flash的Continuous read模式

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是在FDCB里使能串行NOR Flash的Continuous read模式. 前面关于串行Flash传输时序的文章 <Fast R ...

  4. 痞子衡嵌入式:在i.MXRT启动头FDCB里使能串行NOR Flash的QPI/OPI模式

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是在FDCB里使能串行NOR Flash的QPI/OPI模式. 我们知道 Flash 读时序里有五大子序列 CMD + ADDR + MO ...

  5. 痞子衡嵌入式:轻松为i.MXRT设计更新Segger J-Link Flash下载算法文件

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是为i.MXRT设计更新Segger J-Link Flash下载算法文件. 想要在Flash中调试,基本是离不开Flash下载算法的,毕 ...

  6. 痞子衡嵌入式:一种i.MXRT下从App中进入ROM串行下载模式的方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT下在App中利用ROM API进ISP/SDP模式的方法. 我们知道i.MXRT系列分为两大阵营:CM33内核的i.MXRT ...

  7. 痞子衡嵌入式:恩智浦MCU安全加密启动一站式工具NXP-MCUBootUtility用户指南

    NXP MCU Boot Utility English | 中文 1 软件概览 1.1 介绍 NXP-MCUBootUtility是一个专为NXP MCU安全加密启动而设计的工具,其特性与NXP M ...

  8. 痞子衡嵌入式:在i.MXRT1170上启动含DQS的Octal Flash可不严格设Dummy Cycle (以MT35XU512为例)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是Octal或Hyper Flash上DQS信号与Dummy Cycle联系. 关于在 i.MXRT 上启动 NOR Flash 时如何设 ...

  9. 痞子衡嵌入式:i.MXRT中FlexSPI外设对AHB Burst Read特性的支持

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是FlexSPI外设对AHB Burst Read特性的支持. 痞子衡之前写过一篇关于FlexSPI LUT的文章 <从头开始认识i ...

随机推荐

  1. css & focus-within & pseudo class

    css & focus-within & pseudo class demo :focus-within https://developer.mozilla.org/en-US/doc ...

  2. ng 基础

    文档 组件的工作只管用户体验,而不用顾及其它. 它应该提供用于数据绑定的属性和方法,以便作为视图和应用逻辑的中介者 组件应该把诸如从服务器获取数据.验证用户输入或直接往控制台中写日志等工作委托给各种服 ...

  3. Renice INC:解密干型葡萄酒

    市场上,干型葡萄酒往往对比甜型葡萄酒(如甜红.甜白)受到更多葡萄酒爱好者的青睐.在葡萄酒界,大部分的红葡萄酒和白葡萄酒也都是干型的,而且它们的口感往往各有特色,并非千篇一律.今天,就跟随Renice ...

  4. 「NGK每日快讯」12.15日NGK公链第42期官方快讯!

  5. TCP编程详解

    目录 数据包格式 建立连接(三次握手) 数据传输 断开连接(四次挥手) 基础 客户端流程 编码 TCP服务端流程 TCP服务端编码 参考文献 TCP把连接作为最基本的对象,每一条TCP连接都有两个端点 ...

  6. brew安装Nginx

    目录 安装流程 常用命令记录 典型配置方式 查看启动状态是否有报错 php 启动 参考 安装流程 这里使用 brew 来安装软件. 安装 brew install nginx 查看安装信息(经常用到, ...

  7. Arrays.Sort()中的那些排序算法

    本文基于JDK 1.8.0_211撰写,基于java.util.Arrays.sort()方法浅谈目前Java所用到的排序算法,仅个人见解和笔记,若有问题欢迎指证,着重介绍其中的TimSort排序,其 ...

  8. 【转载】Android应用AsyncTask处理机制详解及源码分析

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个 ...

  9. Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件

    封面:洛小汐 作者:潘潘 若不是生活所迫,谁愿意背负一身才华. 前言 上节我们介绍了 < Mybatis系列全解(四):全网最全!Mybatis配置文件 XML 全貌详解 >,内容很详细( ...

  10. 在Linux中安装MariaDB并添加远程访问

    在Linux中安装MariaDB并添加远程访问 最近学习到了数据库部分,因为有一台台式机一台笔记本换着用,就没有把数据库安装在本机,本来打算用之前买的虚拟空间的数据库的,结果速度太慢用起来太难受了,就 ...