探讨i.MX RT下FlexSPI driver实现Flash编程时对于中断支持问题

cathy的头像
cathy 发布于:周一, 02/06/2023 - 09:17 ,关键词:

前段时间有客户在官方社区反映i.MX RT1170下,使用官方SDK里FlexSPI驱动去擦写Flash时不能很好地支持全局中断。

客户项目里用了两块NOR Flash,分别挂在不同的 FlexSPI上,一块Flash用于存储XIP代码(FlexSPI1),另一块Flash用于存储项目资源数据(FlexSPI2),显然这样的设计原理上是没有问题的,那为什么使能了中断会出问题呢?今天就来分析下这个问题:

注: 客户测试的SDK版本为 2.12.1,对应的FlexSPI driver版本为2.3.6

一、为什么擦写Flash时经常需要关全局中断?

在具体分析客户问题之前,我们先来聊聊嵌入式应用里应对NOR Flash的擦写,为何大部分情况下都是要关闭全局中断(这里假设执行代码空间与擦写操作空间在同一个 Flash上,当然是在不同区域),这其实跟如下两个特性有关:

1.1 RWW特性(Read-While-Write)

RWW特性的意思是在Flash执行擦写命令进入Busy 状态期间(Flash内部状态寄存器 WIP位变状态1)还能否继续响应非操作区域的读访问。如果SR[WIP] = 1 时还能够支持读访问,则该Flash 支持RWW,反之则不支持RWW。

1.png

绝大部分Flash都是不支持RWW特性的,这就是为什么Flash擦写操作代码本身是需要重定向到RAM里去执行(尤其是回读SR[WIP]状态的代码)。

对于支持RWW特性的Flash,一般是以Block为单位,Flash擦写操作代码放在BlockX 里执行,则可以操作BlockX以外的其它Block 区域,且不需要做代码重定向。

现在你应该知道对于不支持RWW的Flash为什么擦写时需要关闭全局中断了,因为无法保证中断响应相关代码全都重定向到RAM里了,所以干脆在Flash擦写期间不响应任何中断。

1.2 SCLK Stop特性

SCLK Stop特性的意思是在Flash执行写入命令接受主设备传输过来的Page数据期间,如果总线上SCLK停止(一般情况是FlexSPI这一端的TXFIFO为空或者触发空条件),则Flash能否也暂停接受当前Page数据直到SCLK继续输出从而继续处理剩下的Page数据。

绝大部分Flash是不支持SCLK Stop特性的,因此在MCU端如果传输Page数据,需要一次性连续传输完成,一旦中途被打断,则两次不连续的Page数据传输可能无法得到想要的Page写入结果。这也是为何Flash写入期间我们需要关闭中断。

二、FlexSPI外设写操作设计

关于i.MX RT上的FlexSPI外设基本情况,以前有两篇旧文 《FlexSPI支持在Flash XIP原理》、《FlexSPI支持AHB方式写入Flash》,大家先读一下有个初步了解。

这里想重点说一下FlexSPI关于IPG方式写操作的设计,下图为FlexSPI外设的模块框图,绿色线标出了 IPG 方式写入的通路,这里大家可以看出,其中 IP_TX_FIFO 模块起了重要的数据缓冲作用,驱动里往 FLEXSPI->TFDRx 寄存器写入的 Page 数据会先被装载进 IP_TX_FIFO 里,然后再传输出去。

2.png

不同i.MX RT型号中IP_TX_FIFO大小不一样,目前有三种大小:128、256或1024 Bytes。

对于QuadSPI/OctalSPI NOR Flash来说,Page 大小一般是256 Bytes;对于 HyperBus Flash,Page 大小一般是 512 Bytes。所以在 i.MX RT10xx 上 IP_TX_FIFO 是不足以缓冲整个 Page 的,i.MX RT117x 上可以缓冲 QuadSPI/OctalSPI NOR 类型的 Page,i.MX RT118x/5xx/6xx 上则可以缓冲全部 NOR Flash 类型的 Page。

对于 Page 数据不能全部缓冲的情况,则需要一边传输一边缓冲。

3.png

在具体装载数据进 IP_TX_FIFO 时,主要涉及如下三个 FLEXSPI 寄存器,IP_TX_FIFO 一次只能被填入watermark level大小的数据,想要把全部 Page 数据填进 IP_TX_FIFO,需要分多次装载。只要 FLEXSPI->INTR[IPTXWE] 标志为 0, 即代表 IP_TX_FIFO 剩余空间大于等于 watermark level,那么就可以继续装载。

  • FLEXSPI->IPTXFCR[TXWMRK]  -- 设置一次装载进 IP_TX_FIFO 的数据长度(即 watermark level),8 Bytes为单位
  • FLEXSPI->TFDRx            -- 按 watermark level 长度填入 IP_TX_FIFO 装载数据
  • FLEXSPI->INTR[IPTXWE]     -- 触发 IP_TX_FIFO 的一次装载

4.png

三、客户问题及FlexSPI driver写操作流程

前面铺垫了这么多,终于来到客户遇到的 FlexSPI 驱动对于中断不支持的问题了。因为客户使用了两片Flash,所以不存在 RWW 限制问题,那剩下的原因就跟 SCLK Stop 特性有关,即 IP_TX_FIFO 并没有缓冲全部的 Page,导致 Page 传输过程被中断打断了,然后 IP_TX_FIFO 因为缓冲数据全部发完而使 FlexSPI 模块进入了 SCLK Stop 状态。

我们直接打开fsl_flexspi.c驱动文件,找到跟写操作相关的 FLEXSPI_TransferBlocking() 函数,在函数实现里可以发现,启动写传输时序的控制位 FLEXSPI->IPCMD[TRG] 是在 IP_TX_FIFO 填充动作 FLEXSPI_WriteBlocking() 函数之前被开启的,那这样的实现确实是不能够很好地支持中断的。

5.png

四、如何改进FlexSPI driver支持中断?

知道了原因所在,改起来也很简单。如果是QuadSPI/OctalSPI NOR Flash类型(Page=256 Bytes),在 i.MX RT117x 上,其 IP_TX_FIFO 大小为 256 Bytes,能够缓冲全部的 Page 大小,则可以先调用 FLEXSPI_WriteBlocking() 装载全部的 Page 数据,然后再开启 FLEXSPI->IPCMD[TRG] 去触发写传输时序,这时候就不怕被中断打断了,如下代码所示。

当然下面代码只是一个 workaround 式的实现示例,不是一个完整的解决方案,毕竟 FlexSPI 驱动要适配全部 i.MX RT 型号以及全部类型的 NOR Flash,此外还适用 NAND 型 Flash(Page 一般是 2KB),这时候需要根据情况拆分调用多次 FLEXSPI_WriteBlocking() 函数(不管怎样要保证启动写传输时序前,把 IP_TX_FIFO 先装满)。

status_t FLEXSPI_TransferBlocking(FLEXSPI_Type *base, flexspi_transfer_t *xfer)
{
    // 代码略去

    /* Start Transfer. */
    if ((xfer->cmdType == kFLEXSPI_Write) || (xfer->cmdType == kFLEXSPI_Config))
    {
        result = FLEXSPI_WriteBlocking(base, xfer->data, xfer->dataSize);
        base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
    }
    else if (xfer->cmdType == kFLEXSPI_Read)
    {
        base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
        result = FLEXSPI_ReadBlocking(base, xfer->data, xfer->dataSize);
    }
    else
    {
        base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
    }

    // 代码略去
}

来源:恩智浦MCU加油站

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 39