• 正文
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

Flash工作頻率與Dummy Cycle是怎樣的聯(lián)系?

2024/04/21
4.2萬
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是Flash工作頻率與Dummy Cycle的聯(lián)系。

上一篇文章 《從頭開始認(rèn)識(shí)i.MXRT啟動(dòng)頭FDCB里的lookupTable》,痞子衡帶大家從頭梳理了下i.MXRT下啟動(dòng)頭FDCB里lookupTable的設(shè)計(jì)與實(shí)現(xiàn)細(xì)節(jié),在這個(gè)過程中也簡(jiǎn)單跟大家介紹了串行NOR Flash的工作模式(主要是Fast Read Quad I/O),今天痞子衡在FDCB設(shè)置范疇下跟大家再進(jìn)一步地探討Flash工作頻率與Dummy Cycle設(shè)置問題。

一、引入Flash工作頻率與Dummy Cycle對(duì)應(yīng)關(guān)系問題

今天我們以i.MXRT1170-EVK上的板載Flash為例,其具體型號(hào)為IS25WP128-JBLE,在Flash數(shù)據(jù)手冊(cè)特性介紹里可以看到這顆Flash最高可以運(yùn)行在133MHz頻率下(SDR模式),并且其Dummy Cycle也是可選的,那么Dummy Cycle是不是可以任意配呢?答案既是也不是,痞子衡先賣個(gè)關(guān)子。

讓我們?cè)賮砘仡櫹逻@顆Flash的Fast Read Quad I/O時(shí)序圖,從時(shí)序圖里可以看到Dummy Cycle默認(rèn)是6(包括Mode Bits時(shí)序一共6個(gè)SCK時(shí)鐘周期),那么默認(rèn)的6個(gè)Dummy Cycle是不是適用全部的Flash工作頻率呢?咱們繼續(xù)看Flash數(shù)據(jù)手冊(cè)。

在數(shù)據(jù)手冊(cè)里找到了下面這張表,Read Dummy Cycle與最大工作頻率的聯(lián)系,從表里可以看到當(dāng)Flash工作在Fast Read Quad I/O模式時(shí),默認(rèn)的6個(gè)Dummy Cycle適用的最大工作頻率是104MHz,即104MHz工作頻率及以下均可以使用默認(rèn)的6個(gè)Dummy Cycle。如果工作頻率高于104MHz,Dummy Cycle應(yīng)相應(yīng)調(diào)大,比如133MHz工作頻率需對(duì)應(yīng)至少9個(gè)Dummy Cycle。

 

二、如何在FDCB里設(shè)置FlexSPI的Dummy Cycle?

根據(jù)上面的分析,如果我們希望i.MXRT1170-EVK上這顆IS25WP128-JBLE啟動(dòng)后工作在133MHz,那么我們需要在FDCB里至少配置9個(gè)Dummy Cycle。因此下述FDCB啟動(dòng)頭里 FLASH_DUMMY_CYCLES 宏應(yīng)設(shè)為9, qspiflash_config.memConfig.serialClkFreq 應(yīng)改為 kFlexSpiSerialClk_133MHz,這樣的設(shè)置對(duì)于i.MXRT端是足夠的,因?yàn)楦暮驠lexSPI外設(shè)確實(shí)可以輸出133MHz的SCK,并且按9個(gè)Dummy Cycle的時(shí)序去讀Flash。

但是把這樣的FDCB啟動(dòng)頭直接下載進(jìn)Flash后發(fā)現(xiàn)i.MXRT無法從Flash正常啟動(dòng),因?yàn)镕lash端的Dummy Cycle還是默認(rèn)的6個(gè)SCK周期,上面的FDCB僅僅是調(diào)整了FlexSPI輸出,并不會(huì)同步調(diào)整Flash端,此時(shí)主從兩端Dummy Cycle數(shù)不匹配,時(shí)序錯(cuò)亂了,傳輸數(shù)據(jù)發(fā)生了錯(cuò)位,應(yīng)用程序當(dāng)然無法啟動(dòng)。

所以Flash這邊需要其他的方式設(shè)置好Dummy Cycle后,上述方式更改的FDCB啟動(dòng)頭才能正常使用。

有一個(gè)現(xiàn)象需要特別說明一下,如果我們直接修改 qspiflash_config.memConfig.serialClkFreq 到 kFlexSpiSerialClk_133MHz, 但是 FLASH_DUMMY_CYCLES 依舊保持默認(rèn)的6,這樣的FDCB頭下載進(jìn)Flash也可以正常工作的,雖然有點(diǎn)違反Flash數(shù)據(jù)手冊(cè)里的Dummy Cycle規(guī)范,但實(shí)測(cè)下來似乎是沒問題的,那是不是意味著我們根本不需要?jiǎng)幽J(rèn)的Dummy Cycle?其實(shí)不是的,當(dāng)我們使能Flash的 continuous read mode 的時(shí)候,Dummy Cycle不規(guī)范就會(huì)出問題,關(guān)于這點(diǎn)痞子衡會(huì)單獨(dú)寫一篇文章細(xì)聊。

三、如何更改Flash里的Dummy Cycle?

那么Flash里的Dummy Cycle到底怎么改呢?這需要我們繼續(xù)看Flash數(shù)據(jù)手冊(cè),IS25WP128-JBLE內(nèi)部有個(gè)8bit的Read Register,其bit6-bit3是Dummy Cycles設(shè)置,可設(shè)范圍是1-15,并且在寄存器類型里可以看到其有易失性和非易失性兩種屬性,也就是說我們既可以臨時(shí)地改Dummy Cycle(掉電即恢復(fù)默認(rèn)6),也可以永久地改Dummy Cycle(掉電仍保持上一次設(shè)置)。

在IS25WP128-JBLE的指令集表里,可以看到有專門寫Read Register的指令,SRPNV指令是非易失性方式地寫Read Register,SRPV指令是易失性方式去寫Read Register。

分析到這里,問題就變成到底是使用一個(gè)額外的小工程(比如借助SDK里的flexspi example稍微更改下代碼)以SRPNV指令去永久性更改下Flash里的Dummy Cycle再用作i.MXRT啟動(dòng),還是i.MXRT每次啟動(dòng)時(shí)直接借助FDCB啟動(dòng)頭里的設(shè)置用SRPV指令臨時(shí)地更改Flash的Dummy Cycle?從靈活性角度,痞子衡推薦第二種方式,那么在FDCB里應(yīng)該怎么做?痞子衡直接給答案:

// 設(shè)置Dummy Cycle數(shù)
#define FLASH_DUMMY_CYCLES      9
// 寫Read Register時(shí)序在LUT中的index(可自定義位置,但不要占BootROM預(yù)設(shè)的幾個(gè)時(shí)序位置)
#define CMD_LUT_SEQ_IDX_SET_READ_PARAM 7
// BootROM中預(yù)設(shè)的LUT命令時(shí)序的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寄存器配置時(shí)序在LUT中index
            .configCmdSeqs[0] = 
                {
                    .seqNum = 1,
                    .seqId = CMD_LUT_SEQ_IDX_SET_READ_PARAM,
                    .reserved = 0,
                },
            // 設(shè)定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寄存器配置時(shí)序(這個(gè)時(shí)序需要上面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對(duì)FDCB里Flash寄存器配置的處理

說到BootROM對(duì)FDCB的處理,大家有必要先回顧下痞子衡寫過的一篇舊文 《深入i.MXRT1050系列ROM中串行NOR Flash啟動(dòng)初始化流程》,文章雖然是針對(duì)i.MXRT1050寫的,但基本流程也差不多適用i.MXRT1170,在文中的 2.6 小節(jié)第二次初始化里,我們?cè)谏厦鍲DCB里的關(guān)于Flash寄存器的額外配置操作才會(huì)被處理,代碼大致如下:

status_t flexspi_init(uint32_t instance, flexspi_mem_config_t *config)
{
    status_t status = kStatus_InvalidArgument;

    // 決定是否用30MHz SDR的安全時(shí)鐘來做FlexSPI初始化
    bool needSafeFreq = (config->deviceModeCfgEnable || config->configCmdEnable) &&
                          ((config->controllerMiscOption & (1 << kFlexSpiMiscOffset_SafeConfigFreqEnable)));
    flexspi_clock_gate_disable(instance);
    flexspi_iomux_config(instance, config);
    if (needSafeFreq)
    {
        flexspi_clock_config(instance, kFlexSpiSerialClk_SafeFreq, kFlexSpiClk_SDR);
    }

    flexspi_clock_gate_enable(instance);
    // 省略代碼片段,F(xiàn)lexSPI模塊本身的完整初始化
    // ...
    flexspi_swreset(base);
    
    // 實(shí)現(xiàn)Flash配置寄存器寫入操作
    if (config->configCmdEnable)
    {
        // Port A1/A2/B1/B2如使能則全寫一遍
        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;
            }
        }
    }

    if (needSafeFreq)
    {
        // 重新配回指定的FlexSPI時(shí)鐘
        flexspi_clock_config(instance, config->serialClkFreq, flexspi_is_ddr_mode_enable(config));
    }

    return status;
}

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];

            // 這里需要調(diào)用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))
            {
                // 這里需要調(diào)用READ STATUS指令
                flexspi_device_wait_busy(instance, config, false, baseAddr);
            }
            else
            {
                flexspi_sw_delay_us(config->waitTimeCfgCommands * 100UL);
            }
        }
    }
}

至此,F(xiàn)lash工作頻率與Dummy Cycle的聯(lián)系痞子衡便介紹完畢了,掌聲在哪里~~~

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

碩士畢業(yè)于蘇州大學(xué)電子信息學(xué)院,目前就職于恩智浦(NXP)半導(dǎo)體MCU系統(tǒng)部門,擔(dān)任嵌入式系統(tǒng)應(yīng)用工程師。痞子衡會(huì)定期分享嵌入式相關(guān)文章