痞子衡嵌入式:16MB以上NOR Flash使用不当可能会形成软复位后i.MXRT没法正常启动


  你们好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给你们分享的是i.MXRT上使用16MB以上NOR Flash软复位没法正常启动问题的分析解决经验缓存

  痞子衡这几天在支持一个i.MXRT1050客户项目,客户遇到了软复位没法从32MB NOR Flash从新启动的问题。这个客户是作医疗设备的,已经基于i.MXRT作出一款成功的产品了,因此客户其实有丰富的i.MXRT使用经验。目前调试的项目是客户的第二款产品,这个软复位没法启动问题已经困扰他们好久,但问题毕竟不是特别紧急,不影响他们开发进度,因此耽搁至今。此次客户趁着出差苏州参加劳特巴赫TRACE32调试器培训机会,让痞子衡现场帮他们定位问题,通过一番调试和分析,痞子衡终于成功地解决了问题,特此将问题解决的全过程记录下来,供你们参考。微信

1、问题描述

  在描述问题前,首先给你们介绍下客户的项目设计,底下是客户硬件简图。客户选用的i.MXRT1052做为主控,挂载了两个QSPI Flash,FlexSPI接口链接的32MB Flash用于启动和存放静态图片资源(只须要读便可),LPSPI接口链接的1MB Flash用于存放运行时状态数据(须要读写),此外板子链接了一个显示屏,因此还挂载一片SDRAM用于显示缓存,其实SDRAM除了显示缓存功能以外,还用于执行App(QSPI Flash里的App会自加载到SDRAM执行)。app

  有必要重点介绍下QSPI Flash启动设计细节,客户选用的Flash型号是ISSI的IS25WP256D,这是一款容量256Mb的四线串行Flash。客户启动流程设计的挺复杂,芯片上电以后,BootROM负责从Flash中XIP启动L2 loader程序,L2 loader运行后从Flash中选出最新的一份Boot程序(A/B是双备份),将其加载到SDRAM中执行。Boot程序运行后作一些系统初始化工做,而后直接跳转到App中执行(XIP),App才是最终的客户应用程序,这个应用程序会完成往SDRAM的自拷贝以及跳转执行。函数

  客户的App实际大小接近5MB,对于嵌入式程序来讲,这个体量至关大了,这也是为何客户须要借助专业的劳特巴赫TRACE32调试器来分析定位程序逻辑设计问题。从下图还能够看到从0x60800000开始,Flash中还存放了一些静态图片资源,客户项目有显示屏,Flash里放一些固定图片数据方便UI切换。工具

  介绍完客户的项目设计,如今描述客户的软复位没法从新启动问题。其实这个问题现象很简单,就是每次从新上电启动,程序都是能够正常运行的,可是一旦使用按键软复位(ONOFF Reset),系统就会有必定几率起不来(几率很大,很容易复现),调试器连上去会发现PC停留在BootROM里,这意味着此时BootROM没能正常启动L2 loader。测试

2、问题分析

  让咱们来分析一下问题,这个问题要从两方面来考虑:1、板子上芯片的POR和软复位的区别;2、软复位没法启动是几率性的,所以痞子衡想到了以下四处疑点:flex

  1. 两种复位下主芯片内部非易失寄存器状态的区别是否对BootROM运行产生了影响?
  2. 两种复位下主芯片内FlexSPI这个模块状态是否有区别?
  3. 两种复位下外挂Flash芯片状态是否有区别?
  4. 客户App代码里是否有某种操做致使了几率性问题的发生?

  由于每次都是软复位从新启动出问题,因此客户板级供电设计不在疑点范围内。虽然问题都表如今BootROM无法加载L2 loader执行,但BootROM自己缺陷也不是咱们主要考虑的方向,毕竟BootROM是固化在芯片内部的,可靠性有必定保证。咱们首先要把疑点放在几率性以及两种复位的差别上,那么咱们从哪里开始着手测试?ui

  痞子衡想的是先从第4个疑点开始下手,缘由是前3个疑点本质上都由第4个疑点引发的,客户代码的执行可能会改主芯片内部非易失性寄存器,也同时会操做FlexSPI模块去访问外部Flash,它是问题的引爆点。.net

3、开始测试

3.1 对比非易失性寄存器

  i.MXRT内部有一些非易失性寄存器(好比IOMUXC_GPR寄存器组,SRC寄存器等),这些寄存器仅在POR时才会被复位,而普通软复位是不会改变其状态的。客户App代码近5MB,若是是去肉眼排查是否操做了非易失性寄存器,不免有疏漏。最简单的方法就是在正常启动和非正常启动时分别用调试器将这些寄存器的值所有保存下来,而后使用文本工具去对比。经测试,两种状况下,这些非易失性寄存器并没有区别,所以这个疑点被排除。设计

3.2 逐步精简App代码

  如今咱们开始逐步精简App代码,因为客户代码涉及机密,因此精简的工做由客户来作,固然客户也最清楚如何去精简他们本身的代码。一番测试下来,咱们发现App代码里只要不去读存在Flash里的静态图片数据,就不会存在软复位没法从新启动问题,看起来咱们已经找到线索了。

4、缘由分析

  问题出在App代码里读存在Flash里的静态图片数据,这意味着App里可能用了特殊的读Flash方法改变了Flash状态,而且这个Flash状态是非易失性的。谜团接近解开了,痞子衡让客户公布了他们的L2 loader里的FDCB配置头以及App里的读Flash图片的代码实现:

4.1 L2 loader的FDCB

  先来看客户的FDCB启动头,客户仅让BootROM配置Flash工做于50MHz,而且是1bit SDR Fast Read(命令是0x0B),这是标准3字节地址读,所以配置成功后经过AHB总线最大可访问16MB之内的Flash空间。由于客户的L2 loader很小,且存储在Flash的起始地址,因此这样的配置对于启动而言没有问题。

4.2 App读Flash实现

  再来看客户实现的读Flash函数BigCapRead(),根据前面的介绍,静态图片数据是从0x60800000处开始存储的,所以0x60800000 - 0x60FFFFFF范围内的8MB数据是能够直接AHB读,可是0x61000000地址以后的数据在上述BootROM的配置下没法直接访问,这也是为何客户写了BigCapRead()函数,这个函数会根据传入的地址范围来判断数据是在低16MB空间仍是高16MB空间,而后对地址空间作了一个切换。

#define FLASH_BIG_CAP_SIZE (0x1000000)

static status_t flexspi_nor_select_segment(uint32_t base, uint8_t seg)
{
    qspi_transfer_t flashXfer;
    status_t status = Success;
    uint32_t writeValue = 0x00;
    uint32_t readValue = 0x00;

    flexspi_nor_write_enable(base, 0, true);

    flexspi_nor_read_volatilebankaddr_reg(base, &readValue);
    if ((readValue & 0x01) == (seg & 0x1))
    {
        return Success;
    }

    writeValue = seg & 0x1;
    flexspi_nor_write_volatilebankaddr_reg(base, writeValue);

    flexspi_nor_read_volatilebankaddr_reg(base, &readValue);
    if (readValue != writeValue)
    {
        return Failure;
    }

    flexspi_nor_wait_bus_busy(base);
    return Success;
}

static UINT32 BigCapRead(struct flash_dev* dev, UINT32 start_addr, UCHAR *buffer, UINT32 size)
{
    UINT32 tempLen = 0;
    UINT32 result = 0;
    if (start_addr >= FLASH_BIG_CAP_SIZE)
    {
        flexspi_nor_select_segment(dev->base, 1);
        start_addr = start_addr - FLASH_BIG_CAP_SIZE;
        result = Read(dev, start_addr, buffer, size);
    }
    else
    {
        if (start_addr + size < FLASH_BIG_CAP_SIZE)
        {
            flexspi_nor_select_segment(dev->base, 0);
            result = Read(dev, start_addr, buffer, size);
        }
        else
        {
            tempLen = FLASH_BIG_CAP_SIZE - start_addr;
            flexspi_nor_select_segment(dev->base, 0);
            result = Read(dev, start_addr, buffer, tempLen);
            flexspi_nor_select_segment(dev->base, 1);
            result = Read(dev, 0, buffer + tempLen, size - tempLen);
        }
    }
    return result;
}

4.3 关于Flash的3/4字节地址

  对于16MB以上空间的Flash,总会面临3/4字节地址访问的问题,JESD216规定了3/4字节地址访问标准命令,对于3字节地址Fast Read,其命令是FRD(0x0B);而对于四字节地址Fast Read,其命令是4FRD(0x0C)。对于一个32MB的Flash,若是仅须要访问低16MB空间,可使用FRD;若是须要访问高16MB空间,则须要使用4FRD。

  4FRD相比FRD多传输了一字节地址,对于地址连续的大块数据访问,这个1字节地址影响不太,可是若是是执行代码或者非连续数据访问,4FRD相比FRD仍是有效率上的下降的,对于这个问题,不一样的厂家提供了不一样的解决方案。

  客户选用的这款Flash来自ISSI,ISSI的解决方案是在Flash内部增长一个Bank Address Register,其bit0用于实时切换低Bank和高Bank(而且这个位是非易失性的,仅POR才会复位,引脚reset没法复位!),当bit0值为0时,FRD命令访问的是低16MB空间,而bit0置1后,FRD命令此时实际访问的是高16MB空间(从AHB地址上看不出这个变化)。

4.4 解决方案

  看到这,这个软复位没法重启问题真相大白了,是因为这颗Flash里的内部非易失寄存器BAR[0]的操做致使的,若是软复位时间点刚好在App读了高16MB空间(Bank1)里的数据以后,此时Bank发生了切换,软复位后BootROM去启动时没法读到存在低16MB空间(Bank0)的有效的L2 loader。若是软复位时间点发生在App正在读低16MB空间的数据,那下次仍是能够正常启动,这就是几率性启动失败的缘由。

  缘由调查清楚了,问题解决方法就很简单了,将L2 loader里的FDCB头用4FRD代替FRD,这样BootROM配置完成以后,能够直接AHB读所有的32MB空间,不须要切换Bank,所以App里的BigCapRead()函数里的Bank切换操做能够删掉。

  这个经验也告诉了咱们,当使用16MB以上Flash做为启动设备时,必定要当心处理好3/4字节地址访问问题,否则就可能出现启动问题。

  至此,i.MXRT上使用16MB以上NOR Flash软复位没法正常启动问题的分析解决经验痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

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

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

相关文章
相关标签/搜索