精品国产一区在线_av无码中文字幕无码王_天海翼三点刺激高潮不停_好硬好大好爽视频_欧美高清一区三区在线专区_香蕉黄色片

Flash工作頻率與Dummy Cycle是怎樣的聯系?

大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是Flash工作頻率與Dummy Cycle的聯系

痞子衡帶大家從頭梳理了下i.MXRT下啟動頭FDCB里lookupTable的設計與實現細節,在這個過程中也簡單跟大家介紹了串行NOR Flash的工作模式(主要是Fast Read Quad I/O),今天痞子衡在FDCB設置范疇下跟大家再進一步地探討Flash工作頻率與Dummy Cycle設置問題。

一、引入Flash工作頻率與Dummy Cycle對應關系問題

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

讓我們再來回顧下這顆Flash的Fast Read Quad I/O時序圖,從時序圖里可以看到Dummy Cycle默認是6(包括Mode Bits時序一共6個SCK時鐘周期),那么默認的6個Dummy Cycle是不是適用全部的Flash工作頻率呢?咱們繼續看Flash數據手冊。

在數據手冊里找到了下面這張表,Read Dummy Cycle與最大工作頻率的聯系,從表里可以看到當Flash工作在Fast Read Quad I/O模式時,默認的6個Dummy Cycle適用的最大工作頻率是104MHz,即104MHz工作頻率及以下均可以使用默認的6個Dummy Cycle。如果工作頻率高于104MHz,Dummy Cycle應相應調大,比如133MHz工作頻率需對應至少9個Dummy Cycle。

二、如何在FDCB里設置FlexSPI的Dummy Cycle?

根據上面的分析,如果我們希望i.MXRT1170-EVK上這顆IS25WP128-JBLE啟動后工作在133MHz,那么我們需要在FDCB里至少配置9個Dummy Cycle。因此下述FDCB啟動頭里 FLASH_DUMMY_CYCLES 宏應設為9, qspiflash_config.memConfig.serialClkFreq 應改為 kFlexSpiSerialClk_133MHz,這樣的設置對于i.MXRT端是足夠的,因為更改后FlexSPI外設確實可以輸出133MHz的SCK,并且按9個Dummy Cycle的時序去讀Flash。

但是把這樣的FDCB啟動頭直接下載進Flash后發現i.MXRT無法從Flash正常啟動,因為Flash端的Dummy Cycle還是默認的6個SCK周期,上面的FDCB僅僅是調整了FlexSPI輸出,并不會同步調整Flash端,此時主從兩端Dummy Cycle數不匹配,時序錯亂了,傳輸數據發生了錯位,應用程序當然無法啟動。

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

有一個現象需要特別說明一下,如果我們直接修改 qspiflash_config.memConfig.serialClkFreq 到 kFlexSpiSerialClk_133MHz, 但是 FLASH_DUMMY_CYCLES 依舊保持默認的6,這樣的FDCB頭下載進Flash也可以正常工作的,雖然有點違反Flash數據手冊里的Dummy Cycle規范,但實測下來似乎是沒問題的,那是不是意味著我們根本不需要動默認的Dummy Cycle?其實不是的,當我們使能Flash的 continuous read mode 的時候,Dummy Cycle不規范就會出問題,關于這點痞子衡會單獨寫一篇文章細聊。

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

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

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

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

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

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

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

    // 決定是否用30MHz SDR的安全時鐘來做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);
    // 省略代碼片段,FlexSPI模塊本身的完整初始化
    // ...
    flexspi_swreset(base);
    
    // 實現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時鐘
        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];

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

至此,Flash工作頻率與Dummy Cycle的聯系痞子衡便介紹完畢了,掌聲在哪里~~~

聲明:本內容為作者獨立觀點,不代表電子星球立場。未經允許不得轉載。授權事宜與稿件投訴,請聯系:editor@netbroad.com
覺得內容不錯的朋友,別忘了一鍵三連哦!
贊 1
收藏 2
關注 41
成為作者 賺取收益
全部留言
0/200
成為第一個和作者交流的人吧
主站蜘蛛池模板: 欧美精品中文字幕亚洲专区 | 国产成人精品一区二区色戒 | 搞逼逼视频 | 欧美激情亚洲天堂 | 亚洲伊人成人 | 国产成人一区二区三区影院在线 | 国产亚洲精品久久久久久久久久久久 | 香蕉视频在线网站 | 国产综合一 | 中文字幕小视频 | 丰满大乳奶水一区二区 | 日本人真人姓交大视频 | 国产伦精品一区二区三区免费观看 | 无码专区在线观看韩国 | 偷窥xxxx盗摄国产 | 亚洲a∨视频| 爆乳jk美女脱内衣裸体网站 | 伊人久操视频 | 亚洲911精品成人18网站 | 色婷婷久久一区二区三区麻豆 | 精产国产伦理一二三区 | 亚洲精品无码成人A片蜜臀 亚洲永久免费观看 | 黄色激情影院 | 亚洲gay片在线网站 四川少妇BBB凸凸凸BBB | 久久电影免费 | 久久夜夜躁躁精品 | 最近高清无吗免费看 | 国产亚洲精品久久久久久无挡照片 | 欧美3级在线 | 四房播播在线观看 | 色樱桃影院亚洲精品影院 | 好爽又高潮了毛片视频国产 | 日本久久久久久久久久久 | 制服丨自拍丨欧美丨动漫丨 | 玉足女爽爽91 | youjizz中国版在线视频 | 艳魔大战春荡女淫3 | 日韩欧美国产一二三 | 懂色av中文字幕 | 久久夜色精品国产欧美一区麻豆 | 18禁美女裸体网站无遮挡 |