痞子衡嵌入式:i.MXRT中不支持DQS的FlexSPI引腳組連接Flash下載與啟動注意事項


  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是i.MXRT中不支持DQS的FlexSPI引腳組連接Flash下載與啟動注意事項

  最近痞子衡在支持一個印度客戶,這個客戶項目主芯片選擇的是恩智浦 i.MXRT1176,客戶設計板子時選擇將四線 NOR Flash 掛在了主芯片 FlexSPI2 2nd Pinmux( GPIO_SD_B1[05:00] )上,選擇這組引腳當然是可以的,但客戶在使用恩智浦配套下載工具以及 SDK 例程時發現無法正常使用,體驗上跟默認 FlexSPI1 1st Pinmux( GPIO_SD_B2[11:05] )掛載 Flash 完全不同,這其實跟 FlexSPI DQS 信號設計有關,且聽痞子衡道來:

一、FlexSPI的DQS信號作用

  關於 DQS 信號,痞子衡寫過一篇文章 《串行NOR Flash的DQS信號功能簡介》,裏面介紹了 DDR、Octal Flash、Hyper Flash 芯片上的 DQS 信號作用,文章中沒有提及四線 QSPI Flash,因為一般的四線 QSPI Flash 芯片並沒有 DQS 信號引腳。FlexSPI 模塊的 DQS 信號當然可以用來連接 Octal Flash、Hyper Flash 上的 DQS 引腳,那麼對於四線 QSPI Flash,這個 DQS 信號就沒有作用了嗎?其實不是的。

  我們翻開 i.MXRT1170 的數據手冊(Data Sheet)找到 FlexSPI parameters 小節里的如下內容,就很清楚了。FlexSPIn_MCR0[RXCLKSRC] 位對應了三種 DQS 信號源設置:0x0 – Dummy read strobe looped back internally 設置即完全不用 DQS 引腳(可作它用),對應最高 60MHz SDR 訪問速度;0x1 – Dummy read strobe looped back through the DQS pad 設置即從 DQS 引腳上迴環,因此 DQS 引腳需要懸空,對應最高 133MHz SDR 訪問速度;0x3 – Read strobe from memory device DQS pad 設置即接到存儲芯片 DQS 引腳上,對應最高 166MHz SDR 訪問速度;

• Dummy read strobe generated by FlexSPI controller and looped back internally (FlexSPIn_MCR0[RXCLKSRC] = 0x0)
• Dummy read strobe generated by FlexSPI controller and looped back through the DQS pad (FlexSPIn_MCR0[RXCLKSRC] = 0x1)
• Read strobe provided by memory device and input from DQS pad (FlexSPIn_MCR0[RXCLKSRC] = 0x3)

二、哪些FlexSPI引腳組不支持DQS?

  目前恩智浦已量產的所有 i.MXRT 型號里(RT500/600/1010/1015/1020/1050/1060/1160/1170),大部分的 FlexSPI 引腳組合都是包含 DQS 信號設計的,只有如下兩個例外。沒有 DQS 信號的引腳組合連 Flash 時,應配置 FlexSPIn_MCR0[RXCLKSRC] 為 0x0 – looped back internally。

2.1 i.MXRT600 FlexSPI0 PortB

  見 《FlexSPI NOR連接方式大全(RT600)》 一文第三節、單Flash連接方式里的第四種Flash連接方式。

2.2 i.MXRT1160/1170 FlexSPI2 2nd PortA

  見 《FlexSPI NOR連接方式大全(RT1160/1170)》 一文第二節、單Flash連接方式里第一張表格里的最後一組連接方式。

三、使能沒有DQS的FlexSPI連接的Flash

  對於不含 DQS 信號的 FlexSPI 引腳組合,使用恩智浦相關工具操作連接在其上的 NOR Flash 是需要做一些改動的,我們以 i.MXRT1170-Validation 板卡為例來介紹具體改動。

  i.MXRT1170-Validation 板卡是專供恩智浦內部使用的,分為 CPU1/2/3/4 四款,每款的硬件連接不同,其中 CPU2 板卡在 FlexSPI2 2nd PortA 上連接了一顆鎂光的 MT25QL128:

3.1 SDK中FlexSPI擦寫Flash例程改動

  我們現在打開 SDK 里的 FlexSPI 例程,這個例程是針對 MIMXRT1170-EVK 板卡寫的,在 EVK 上 NOR Flash 默認是連在 FlexSPI1 1st PortA 上的,因此我們需要對例程做一些改動。

例程路徑:\SDK_2.x.x_MIMXRT1170-EVK\boards\evkmimxrt1170\driver_examples\flexspi\nor\polling_transfer\cm7

  首當其衝的改動當然是 pin_mux.c 文件里的 BOARD_InitPins() 函數,需要將 FlexSPI1 1st Pinmux 換成 FlexSPI2 2nd Pinmux:

void BOARD_InitPins(void) 
{
    CLOCK_EnableClock(kCLOCK_Iomuxc);

    //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_05_FLEXSPI1_A_DQS, 1U);
    //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_06_FLEXSPI1_A_SS0_B, 1U;
    //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_07_FLEXSPI1_A_SCLK, 1U);
    //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_08_FLEXSPI1_A_DATA00, 1U);
    //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_09_FLEXSPI1_A_DATA01, 1U);
    //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_10_FLEXSPI1_A_DATA02, 1U);
    //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_11_FLEXSPI1_A_DATA03, 1U);
    //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_05_FLEXSPI1_A_DQS, 0x0AU);
    //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_06_FLEXSPI1_A_SS0_B, 0x0AU);
    //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_07_FLEXSPI1_A_SCLK, 0x0AU);
    //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_08_FLEXSPI1_A_DATA00, 0x0AU);
    //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_09_FLEXSPI1_A_DATA01, 0x0AU);
    //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_10_FLEXSPI1_A_DATA02, 0x0AU);
    //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_11_FLEXSPI1_A_DATA03, 0x0AU);
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_00_FLEXSPI2_A_SS0_B, 1U;
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_01_FLEXSPI2_A_SCLK, 1U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_02_FLEXSPI2_A_DATA00, 1U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_03_FLEXSPI2_A_DATA01, 1U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_04_FLEXSPI2_A_DATA02, 1U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_05_FLEXSPI2_A_DATA03, 1U);
    IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_00_FLEXSPI2_A_SS0_B, 0x0AU);
    IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_01_FLEXSPI2_A_SCLK, 0x0AU);
    IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_02_FLEXSPI2_A_DATA00, 0x0AU);
    IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_03_FLEXSPI2_A_DATA01, 0x0AU);
    IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_04_FLEXSPI2_A_DATA02, 0x0AU);
    IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_05_FLEXSPI2_A_DATA03, 0x0AU);
}

  然後再改一下 app.h 文件里的宏定義,從 FlexSPI1 換到 FlexSPI2,並相應調整 Flash 屬性(EVK 上是 IS25WP128,Validation 板上是 MT25QL128),以及更新 flexspi_clock_init() 函數:

//#define EXAMPLE_FLEXSPI FLEXSPI1
//#define EXAMPLE_FLEXSPI_AMBA_BASE FlexSPI1_AMBA_BASE
//#define EXAMPLE_FLEXSPI_CLOCK kCLOCK_Flexspi1
#define EXAMPLE_FLEXSPI FLEXSPI2
#define EXAMPLE_FLEXSPI_AMBA_BASE FlexSPI2_AMBA_BASE
#define EXAMPLE_FLEXSPI_CLOCK kCLOCK_Flexspi2

static inline void flexspi_clock_init(void)
{
    // 在 BOARD_BootClockRUN() 函數里給 FlexSPI 設置的時鐘源是 OSC_RC_48M_DIV2 / 2 即 12MHz
    // 這裡沒有再次分頻,因此 FlexSPI root clock 就是 12MHz
    //CLOCK_SetRootClockDiv(kCLOCK_Root_Flexspi1, 2);
    //CLOCK_SetRootClockMux(kCLOCK_Root_Flexspi1, 0);
    CLOCK_SetRootClockDiv(kCLOCK_Root_Flexspi2, 2);
    CLOCK_SetRootClockMux(kCLOCK_Root_Flexspi2, 0);
}

  上面都是大家能意識到的改動,但其實最容易被忽略的改動是 flexspi_nor_flash_ops.c 文件里的 flexspi_nor_flash_init() 函數,config.rxSampleClock 設置必須要改成 kFLEXSPI_ReadSampleClkLoopbackInternally 才行。代碼全部改完之後下載運行就可以正常擦寫 Flash 了。

void flexspi_nor_flash_init(FLEXSPI_Type *base)
{
    // 省略部分代碼 ...

    flexspi_clock_init();

    flexspi_config_t config;
    FLEXSPI_GetDefaultConfig(&config);

    config.ahbConfig.enableAHBPrefetch    = true;
    config.ahbConfig.enableAHBBufferable  = true;
    config.ahbConfig.enableReadAddressOpt = true;
    config.ahbConfig.enableAHBCachable    = true;
    //config.rxSampleClock                  = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad;
    config.rxSampleClock                  = kFLEXSPI_ReadSampleClkLoopbackInternally;
    FLEXSPI_Init(base, &config);

    // 省略部分代碼 ...
}

3.2 SDK中Flashloader工程使用

  現在我們再來用一下 SDK 里的 flashloader 工程,這個應用程序可以與恩智浦專用命令行上位機 blhost.exe 進行交互,工程需要用調試器下載進主芯片內部 RAM 運行,具體步驟可參考 《Flashloader初體驗(blhost)》

工程路徑:\SDK_2.x.x_MIMXRT1170-EVK\boards\evkmimxrt1170\bootloader_examples\flashloader\cm7

  flashloader 運行起來之後,使用 blhost 工具按序執行下列命令,也一樣能對 Flash 進行擦寫:

blhost -u -- get-property 1

# 選中 FlexSPI2
blhost -u -- fill-memory 0x20000000 4 0xcf900002
blhost -u -- configure-memory 9 0x20000000

# 配置 NOR Flash(forced internal DQS)
blhost -u -- fill-memory 0x20000000 4 0xc1000053  # 其中bit[7:4]是關鍵設置!!!
blhost -u -- fill-memory 0x20000004 4 0x00110000
blhost -u -- configure-memory 9 0x20000000
blhost -u -- get-property 25 9

# 下載包含 IVT 頭的 App
blhost -u -- flash-erase-region 0x60000000 0x8000
blhost -u -- fill-memory 0x20000000 4 0xf000000f
blhost -u -- configure-memory 9 0x20000000
blhost -u -- write-memory 0x60001000 ivt_app.bin

  上面命令序列里第二條 fill-memory 命令的參數 0xc1000053 是關鍵,從 flashloader 源代碼里看它其實是在設置 serial_nor_config_option_t.option0.B.misc_mode 為 kSerialNorEnhanceMode_InternalLoopback,這個設置對於四線 QSPI Flash 而言就是設 config->memConfig.readSampleClkSrc 為 kFlexSPIReadSampleClk_LoopbackInternally:

enum
{
    kSerialNorEnhanceMode_Disabled = 0,
    kSerialNorEnhanceMode_0_4_4_Mode = 1,
    kSerialNorEnhanceMode_0_8_8_Mode = 2,
    kSerialNorEnhanceMode_DataOrderSwapped = 3,
    kSerialNorEnhanceMode_2ndPinMux = 4,
    kSerialNorEnhanceMode_InternalLoopback = 5,
};

status_t parse_sfdp(uint32_t instance,
                    flexspi_nor_config_t *config,
                    jedec_info_table_t *tbl,
                    serial_nor_config_option_t *option)
{
    status_t status = kStatus_InvalidArgument;
    do
    {
        // 省略部分代碼...
        uint8_t misc_mode = option->option0.B.misc_mode;
        if (misc_mode == kSerialNorEnhanceMode_Disabled)
        {
            // 省略部分代碼...
        }
#if FLEXSPI_ENABLE_NO_CMD_MODE_SUPPORT
        else if (misc_mode == kSerialNorEnhanceMode_0_4_4_Mode)
        {
            // 省略部分代碼...
        }
#endif // FLEXSPI_ENABLE_NO_CMD_MODE_SUPPORT
        else if (misc_mode == kSerialNorEnhanceMode_InternalLoopback)
        {
            config->memConfig.readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally;
        }
        else
        {
            // Do nothing
        }
        // 省略部分代碼...
    } while (0);
    return status;
}

3.3 下載工具MCUBootUtility配置

  flashloader 的使用對一般客戶來說太複雜了,還是圖形化工具 MCUBootUtility 更方便,打開這個工具,按如下配置(主要就是圖中藍框圈起來的 Misc Mode 設置),也可以正常擦寫 Flash。熟悉這個工具原理的朋友應該知道它底層依賴得就是 3.2 節里的 flashloader 與 blhost。

3.4 SDK例程里的FDCB啟動頭改動

  最後就是 SDK 里全部例程的 XIP build 都需要一個 FDCB 頭,這個頭定義在 evkmimxrt1170_flexspi_nor_config.c 文件里,這裡也要改一下 readSampleClkSrc 和 serialClkFreq 配置才行。

const flexspi_nor_config_t qspiflash_config = {
    .memConfig =
        {
            .tag              = FLEXSPI_CFG_BLK_TAG,
            .version          = FLEXSPI_CFG_BLK_VERSION,
            //.readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad,
            .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally,
            .csHoldTime       = 3u,
            .csSetupTime      = 3u,
            .controllerMiscOption = 0x10,
            .deviceType           = kFlexSpiDeviceType_SerialNOR,
            .sflashPadType        = kSerialFlash_4Pads,
            //.serialClkFreq        = kFlexSpiSerialClk_133MHz,
            .serialClkFreq        = kFlexSpiSerialClk_60MHz,
            .sflashA1Size         = 16u * 1024u * 1024u,
            .lookupTable =
                {
                    // Read LUTs
                    FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18),
                    FLEXSPI_LUT_SEQ(MODE8_SDR, FLEXSPI_4PAD, 0x00, DUMMY_SDR, FLEXSPI_4PAD, 0x04),
                    FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_4PAD, 0x04, 0, 0, 0),
                },
        },
    .pageSize           = 256u,
    .sectorSize         = 4u * 1024u,
    .ipcmdSerialClkFreq = 0x1,
    .blockSize          = 256u * 1024u,
    .isUniformBlockSize = false,
};

  至此,i.MXRT中不支持DQS的FlexSPI引腳組連接Flash下載與啟動注意事項痞子衡便介紹完畢了,掌聲在哪裡~~~

歡迎訂閱

文章會同時發佈到我的 博客園主頁CSDN主頁知乎主頁微信公眾號 平台上。

微信搜索”痞子衡嵌入式“或者掃描下面二維碼,就可以在手機上第一時間看了哦。