大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是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主頁、知乎主頁、微信公眾號 平臺上。
微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。