I.MX RT1170之FlexSPI(3):NOR Flash手册分析和参数配置详解

在上一节中,我们分析了FlexSPI的相关参数:LUT表格的组成和FlexSPI结构体配置,这一节就以WINBOND的W25Q256JV NOR Flash为例,看一下如何根据这个NOR Flash的数据手册配置FlexSPI接口。

1 SDK代码

上一节我们详细分析了FlexSPI的相关参数,但大部分的参数实际上我们保持默认就行,只需要配置一些典型的参数即可。所以首先就来看一下在官方SDK中是如何配置NOR Flash的,用到了哪些参数。

flexspi_device_config_t deviceconfig = {
    .flexspiRootClk       = 12000000,
    .flashSize            = FLASH_SIZE,
    .CSIntervalUnit       = kFLEXSPI_CsIntervalUnit1SckCycle,
    .CSInterval           = 2,
    .CSHoldTime           = 3,
    .CSSetupTime          = 3,
    .dataValidTime        = 0,
    .columnspace          = 0,
    .enableWordAddress    = 0,
    .AWRSeqIndex          = 0,
    .AWRSeqNumber         = 0,
    .ARDSeqIndex          = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD,
    .ARDSeqNumber         = 1,
    .AHBWriteWaitUnit     = kFLEXSPI_AhbWriteWaitUnit2AhbCycle,
    .AHBWriteWaitInterval = 0,
};

void flexspi_nor_flash_init(FLEXSPI_Type *base)
{
    flexspi_config_t config;
    /* To store custom's LUT table in local. */
    uint32_t tempLUT[CUSTOM_LUT_LENGTH] = {0x00U};

    /* Copy LUT information from flash region into RAM region, because LUT update maybe corrupt read sequence(LUT[0])
     * and load wrong LUT table from FLASH region. */
    memcpy(tempLUT, customLUT, sizeof(tempLUT));

    flexspi_clock_init();

    /*Get FLEXSPI default settings and configure the flexspi. */
    FLEXSPI_GetDefaultConfig(&config);

    /*Set AHB buffer size for reading data through AHB bus. */
    config.ahbConfig.enableAHBPrefetch    = true;
    config.ahbConfig.enableAHBBufferable  = true;
    config.ahbConfig.enableReadAddressOpt = true;
    config.ahbConfig.enableAHBCachable    = true;
    config.rxSampleClock                  = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad;
    FLEXSPI_Init(base, &config);

    /* Configure flash settings according to serial flash feature. */
    FLEXSPI_SetFlashConfig(base, &deviceconfig, FLASH_PORT);

    /* Update LUT table. */
    FLEXSPI_UpdateLUT(base, 0, tempLUT, CUSTOM_LUT_LENGTH);

    /* Do software reset. */
    FLEXSPI_SoftwareReset(base);
}

可以看到,FlexSPI的配置主要分为四步:时钟配置、FlexSPI特性配置、FlexSPI Flash配置和LUT表格配置。下面就来分析这四个步骤。

2 NOR Flash配置流程

2.1 时钟配置

FlexSPI的时钟需要正确配置,一是要看NOR Flash最高支持的读写频率,二是要看FlexSPI接口支持的最高频率。
(1)RT1176 FlexSPI时钟频率
这个再手册中似乎没有介绍,即FlexSPI的最高频率取决于其时钟源,FlexSPI的时钟源有以下几种,如下图所示:
在这里插入图片描述
可以看到,最高支持的频率为SYS_PLL2_CLK的528MHz。另外对于FlexSPI1的Secondary Pin Group来说,该组的最大支持频率为100MHz。
在这里插入图片描述

  • 图中写的是最大的Flash频率,实际上对于HyperRAM也是一样的有这个限制。

(2) W25Q256JV clock frequency
在这里插入图片描述
It can be seen that when the NOR Flash is used as the most basic SPI half-duplex, there is 133MHz; for Dual I/O, that is DI, and DOfor data transmission at the same time, the rate can reach 266MHz; and for Quad I/O, that is, on Dual I/Othe basis of There are also two I/O pins, the rate can reach 532MHz.

(3) Selection of clock frequency
We can choose according to the connection of hardware, for example, if it is Quad I/Oconnected, you can choose a higher frequency. But we still choose a lower frequency safely in the stage of program adjustment, here we choose 100MHz.

2.2 FlexSPI feature configuration

The next step is to call FLEXSPI_GetDefaultConfigto obtain some default configurations for the FlexSPI feature structure flexspi_config_t. I introduced each parameter in this structure in detail in the previous article. Because there are so many parameters, this default configuration has certain universality. , can be compatible with most FlexSPI devices, here is a brief review, the above codes modify these parameters based on the default parameters:

config.ahbConfig.enableAHBPrefetch    = true;
config.ahbConfig.enableAHBBufferable  = true;
config.ahbConfig.enableReadAddressOpt = true;
config.ahbConfig.enableAHBCachable    = true;
config.rxSampleClock                  = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad;

(1) enableAHBPrefetch: After completing the current AHB burst read, pre-read some additional data into the buffer to speed up subsequent reads, but this will increase power consumption
(2) enableAHBBufferable : Whether to enable AHB write buffer access, during execution After writing a command, return without waiting for its execution to allow subsequent commands to continue to execute, improving the concurrency of the system
(3) enableReadAddressOpt: Control whether to remove the AHB read burst start address alignment limit, if enabled, burst read The address has no byte alignment restrictions
(4) enableAHBCachable: Enable AHB bus cache reading, if it hits, read from the cache, but ensure data consistency
(5) rxSampleClock: The clock source used for reading data, for the four-wire QSPI For NOR Flash, there is generally no DQS pin.

  • DQS ( Data Strobe Signal) data strobe signal, generally there will be this pin in the eight-line storage device, and it is used when SCLK double-edge sampling is used for Flash to notify FlexSPI to receive data

For W25Q256JV, there is no DQS pin, so here we need to set rxSampleClockit as kFLEXSPI_ReadSampleClkLoopbackInternally, that is, the sampling clock signal is generated internally by the FlexSPI controller and fed back in an internal loop. Compared with the Flash of the DQS pin, the sampling rate of the internal loop feedback method will be lower.

2.3 FlexSPI Flash configuration

Then there is a configuration for a specific Flash, the relevant parameters are in flexspi_device_config_t. Take a look at this structure:

typedef struct _flexspi_device_config
{
    uint32_t flexspiRootClk;
    bool isSck2Enabled;
    uint32_t flashSize;
    flexspi_cs_interval_cycle_unit_t CSIntervalUnit;
    uint16_t CSInterval;
    uint8_t CSHoldTime;
    uint8_t CSSetupTime;
    uint8_t dataValidTime;
    uint8_t columnspace;
    bool enableWordAddress;
    uint8_t AWRSeqIndex;
    uint8_t AWRSeqNumber;
    uint8_t ARDSeqIndex;
    uint8_t ARDSeqNumber;
    /* AHB写等待单位 */
    flexspi_ahb_write_wait_unit_t AHBWriteWaitUnit;
    /* AHB写等待间隔,乘以AHB写等待单位得到AHB写等待周期数,即写完后等待外部存储器处理完数据的时间 */
    uint16_t AHBWriteWaitInterval;
    /* 是否启用写入屏蔽。指示在向外部设备写入数据时是否将FLEXSPI的DQS引脚用作写入屏蔽 */
    bool enableWriteMask;
} flexspi_device_config_t;

(1) flexspiRootClk : It needs to be consistent with the clock frequency of FlexSPI set earlier, it is recommended to set a smaller value before it is adjusted.
(2) isSck2Enabled : used in parallel mode, that is, PortA and PortB of FlexSPI are each connected to a Flash on the hardware, The read and write operations of Flash are completed in parallel on Port A and Port B. FlexSPI will automatically merge/split the data read and written by Flash. At this time, the SCK2 clock needs to be enabled, and it is fine to set it to false here.
(3) flashSize : The size of Flash, in KB. For W25Q256JV, there are 256Mb=32MB=32*1024KB.
(4) CSIntervalUnit and CSInterval : The multiplication of the two is the holding time of the CS chip selection line. Some chips (especially FPGA) have certain restrictions on the interval between two consecutive chip selections. For W25Q256JV, there is no such restriction, and 0 is fine, here is a default interval parameter supported by most Flash.
在这里插入图片描述
(5) CSHoldTime and CSSetupTime : That is, the data hold time and setup time, look at the manual of W25Q256JV:
在这里插入图片描述
you can see that the data is sampled on the rising edge of CLK, and the data should be set up at least 1ns before that. After the rising edge of CLK, the data needs to be kept for at least 3ns. Since the FlexSPI frequency we set here is very low, these two parameters can be set casually, and here they are set to a typical value of 3, that is, 3/12000000=250ns, which is completely sufficient.
(6) columnspace : Some devices are addressed by row/column, and the number of bits used for column address addressing needs to be specified. For W25Q256JV, it does not have the concept of column address, and directly uses 32-bit address to address, so the columnspace is set to 0.
在这里插入图片描述
(7) enableWordAddress : Some devices support word addressing. If enabled, the start address and data length of accessing Flash should be 2-byte aligned. If not aligned, FlexSPI will not transmit the lowest bit to the external Flash, that is, Automatically align down.
在这里插入图片描述
For W25Q256JV, there is no related content of word addressing in the manual, it only supports byte addressing.

(8) AWRSeqIndex, AWRSeqNumber, ARDSeqIndex, ARDSeqNumber : the sequence index and number of the write/read sequence.
Among them, the sequence index xxxSeqIndexcorresponds to the instruction index in the LUT table. For some storage devices (such as HyperFlash/HyperRam/Serial NAND), Flash programming access is completed through several command sequences, which means that the AHB command will also trigger the xxxSeqIndexfollowing xxxSeqNumber-1command sequences in the LUT.

Here AWRSeqIndexand AWRSeqNumberboth are set to 0, which means that the operation of AHB directly writing to Flash is prohibited here, and the user can use the IP command to call the sequence in the LUT table to write to Flash. ARDSeqIndexand ARDSeqNumberare set to 1, indicating that index 1 in the LUT table is a sequence of read operations.

(9) AHBWriteWaitUnit and AHBWriteWaitInterval : The multiplication of the two is the waiting time after the Flash write operation. The page program time of W25Q256JV is as follows:
在这里插入图片描述
You can see that there is no minimum limit, and AHBWriteWaitInterval0 will be assigned in the program here.

2.4 LUT table configuration

下面是LUT表格的代码示例,具体格式可以参考LUT表格的组成和FlexSPI结构体配置。这里实现了很多命令,除了读写命令可以在_flexspi_device_config中配置后由AHB访问外,其它的命令需要使用IP指令访问。一般来说,我们在_flexspi_device_config设置一个读命令,这样ROM BootLoader可以从NOR Flash读取程序并运行,而其它的命令,我们就使用IP指令访问。

#define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL        7
#define NOR_CMD_LUT_SEQ_IDX_READ_FAST          13
#define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD     0
#define NOR_CMD_LUT_SEQ_IDX_READSTATUS         1
#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE        2
#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR        3
#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE 6
#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD   4
#define NOR_CMD_LUT_SEQ_IDX_READID             8
#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG     9
#define NOR_CMD_LUT_SEQ_IDX_ENTERQPI           10
#define NOR_CMD_LUT_SEQ_IDX_EXITQPI            11
#define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG      12
#define NOR_CMD_LUT_SEQ_IDX_ERASECHIP          5

const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
    /* Normal read mode -SDR */
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Fast read mode - SDR */
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0B, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ(
        kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

    /* Fast read quad mode - SDR */
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
        kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),

    /* Read extend parameters */
    [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUS] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x81, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

    /* Write Enable */
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Erase Sector  */
    [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xD7, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),

    /* Page Program - single mode */
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Page Program - quad mode */
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Read ID */
    [4 * NOR_CMD_LUT_SEQ_IDX_READID] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

    /* Enable Quad mode */
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x31, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),

    /* Enter QPI mode */
    [4 * NOR_CMD_LUT_SEQ_IDX_ENTERQPI] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x35, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Exit QPI mode */
    [4 * NOR_CMD_LUT_SEQ_IDX_EXITQPI] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_4PAD, 0xF5, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Read status register */
    [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

    /* Erase whole chip */
    [4 * NOR_CMD_LUT_SEQ_IDX_ERASECHIP] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xC7, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
};
  • LUT中的每个Sequence可以有8个指令,如果不及8个指令可以留空,则默认全为0,正好表示停止指令序列:kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00,其中kFLEXSPI_Command_STOPkFLEXSPI_1PAD都是0。

下面就挑两个时序来分析一下,具体的LUT参数含义在LUT表格的组成和FlexSPI结构体配置

(1)Normal read mode -SDR

[4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
   FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
[4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
   FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

来看一下时序图:
在这里插入图片描述

首先我们需要要进入SDR模式,指令码为kFLEXSPI_Command_SDR,参数为0x03。接着,我们需要传输访问的地址,在W25Q256JV中传输行地址kFLEXSPI_Command_RADDR_SDR即可,参数为地址的宽度,为0x18=24。然后就可以调用kFLEXSPI_Command_READ_SDR来读取数据了,参数为读取数据的最小长度,这里填0x04。其中第二个参数是数据传输使用的引脚个数,从图中可以看出都是一次传8位的,所以都设置为kFLEXSPI_1PAD

(2)Page Program - quad mode
quad mode顾名思义就是四线模式,QSPI有三种硬件连接方式及其对应的工作模式:Single-Bit SPI、Dual SPI、Quad SPI。其中Single-Bit SPI就是SPI接口(MISO和MOSI都是单向的),Dual SPI也是四个引脚,只不过它的MISO和MOSI都是双向的,而对于Quad SPI来说,就是在Dual SPI的基础上还多了两根数据线,如下图所示,左边是W25Q256JV的封装,右边是硬件连接方式。
在这里插入图片描述
现在来看一下quad mode下写数据的时序图:
在这里插入图片描述
这个指令允许一次写256字节的数据(写之前要先擦除),LUT时序如下,和前面的读指令很相似,就是kFLEXSPI_Command_WRITE_SDR的pad应该设置为kFLEXSPI_4PAD

[4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
   FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
[4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
   FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

注意,在使用IP指令执行写操作的时候,还需要保证QE位为1且Write Enable指令已经执行,如下图所示:
在这里插入图片描述
这两个操作就对应了LUT表格中的Enable Quad mode和Write Enable两条指令:

/* Enable Quad mode */
[4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
   FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x31, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),
/* Write Enable */
[4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
   FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

手册中这两条指令的描述如下图所示:
在这里插入图片描述
可以看到Write Enable就直接执行0x06指令就行了,而Enable Quad mode要写状态寄存器2,写的长度为0x04,即4个字节,使用IP指令时,将写入内容设置为0x02U(bit1为QE)即可进入Quad mode

3 总结

本文通过W25Q256JV手册解释了使用FlexSPI初始化W25Q256JV NOR Flash的步骤,实际上就是根据手册中的命令来配置LUT表格,其它的一些时序参数在FlexSPI时钟低的时候随便设置都没有问题,因为肯定能满足。实际调这些外部存储器的时候,我的建议是把时钟降低,读寄存器中的Chip ID,这个读对了,再接下去调别的读写时序。

おすすめ

転載: blog.csdn.net/tilblackout/article/details/131526619