大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是
i.MXRT下改造FlexSPI driver以AHB方式去寫(xiě)入NOR Flash 。痞子衡前段時(shí)間寫(xiě)過(guò)一篇 《串行NAND Flash的兩大特性導(dǎo)致其在i.MXRT FlexSPI下無(wú)法XiP》,文章里介紹了 NAND Flash 的 Page Read 等待特性(發(fā)完 Read 命令后需要回讀 Flash 內(nèi)部狀態(tài)寄存器 Busy 位來(lái)判斷 Page 數(shù)據(jù)是否已準(zhǔn)備好)導(dǎo)致其無(wú)法像 NOR Flash 那樣通過(guò) AHB 方式被便捷訪問(wèn),僅能在一個(gè) Page 空間里實(shí)現(xiàn) AHB 讀(前提是在 IPG 方式發(fā)完讀命令以及讀完?duì)顟B(tài)寄存器確保數(shù)據(jù)已經(jīng)準(zhǔn)備好后)。回到 NOR Flash 上,我們可以輕松通過(guò) AHB 方式讀取 Flash 數(shù)據(jù),但寫(xiě)入 Flash 一般都是調(diào)用 FlexSPI 驅(qū)動(dòng)來(lái)實(shí)現(xiàn)(即 IPG 方式),那么有沒(méi)有可能也通過(guò)
“AHB 方式來(lái)寫(xiě)入 Flash” 呢?答案是可以的,但為啥痞子衡加了個(gè)引號(hào),且往下看:
本文以恩智浦 MIMXRT1170-EVK 開(kāi)發(fā)板上主芯片 i.MXRT1176 及其配套板載 Flash 芯片 - 芯成 IS25WP128 為例。 一、Flash寫(xiě)操作流程芯成 IS25WP128 是一顆很典型的四線 QSPI NOR Flash,其寫(xiě)入(編程)時(shí)序是符合 JEDEC216 標(biāo)準(zhǔn)的。簡(jiǎn)單來(lái)說(shuō),一個(gè)完整的寫(xiě)時(shí)序包含三個(gè)獨(dú)立子時(shí)序:Write Enable 時(shí)序 Page Program 時(shí)序 Read Status 時(shí)序。先來(lái)看打頭陣的 Write Enable 子時(shí)序,NOR Flash 內(nèi)部的狀態(tài)寄存器會(huì)有一個(gè)位叫 WEL (Write Enable Latch),這個(gè)位控制著 Flash 的擦寫(xiě)權(quán)限,默認(rèn)值是 0(即不允許擦寫(xiě))。如果想要寫(xiě)入 Flash,必須先通過(guò) Write Enable 命令將 WEL 位臨時(shí)設(shè)為 1(這個(gè)位會(huì)隨著當(dāng)前的擦寫(xiě)命令結(jié)束后自動(dòng)恢復(fù)到 0)。
置位了 WEL 后,便可以傳輸 Page 數(shù)據(jù)給 Flash,這個(gè)子時(shí)序便是 Page Program。Page Program 按命令地址和數(shù)據(jù)傳輸方式不同分為三種:一線 SPI,四線 SPI,QPI,下面是常用的四線 SPI 的時(shí)序圖,命令和地址通過(guò) IO0 傳輸,數(shù)據(jù)通過(guò) IO[3:0] 傳輸。通常來(lái)說(shuō),在這個(gè)時(shí)序里,傳入的地址應(yīng)該是 Page 首地址,寫(xiě)入數(shù)據(jù)長(zhǎng)度應(yīng)該是一個(gè)完整的 Page 大小。但從非 Page 首地址處寫(xiě)入小于一個(gè) Page 長(zhǎng)度的數(shù)據(jù)也是可以的,但有一個(gè)注意點(diǎn)就是不要在這個(gè)時(shí)序里出現(xiàn)跨頁(yè)的現(xiàn)象(如果出現(xiàn),超出當(dāng)前頁(yè)的數(shù)據(jù)會(huì)被放回到該頁(yè)起始地址處)。
Page Program 子時(shí)序結(jié)束后,數(shù)據(jù)還并未真正寫(xiě)入 Flash 內(nèi)存體中,F(xiàn)lash 內(nèi)部控制器只是開(kāi)始處理數(shù)據(jù),這時(shí)候會(huì)有一個(gè)等待時(shí)間(大概0.2ms),F(xiàn)lash 內(nèi)部的狀態(tài)寄存器有一個(gè)位叫 WIP (Write In Progress),這個(gè)位標(biāo)志著數(shù)據(jù)寫(xiě)入狀態(tài)(默認(rèn)值是 0,當(dāng) Page Program 子時(shí)序結(jié)束后,WIP 立即跳為 1),用戶(hù)需要通過(guò) Read Status 子時(shí)序來(lái)實(shí)時(shí)讀取狀態(tài)寄存器的值從而獲知數(shù)據(jù)處理情況。當(dāng) Flash 內(nèi)部狀態(tài)寄存器中的 WIP 位從 1 跳回到 0,便意味著一次完整的寫(xiě)時(shí)序結(jié)束了,主機(jī)可以發(fā)起下一次寫(xiě)時(shí)序。
二、FlexSPI對(duì)寫(xiě)時(shí)序支持痞子衡舊文 《從頭開(kāi)始認(rèn)識(shí)i.MXRT啟動(dòng)頭FDCB里的lookupTable》 里對(duì) FlexSPI 讀時(shí)序介紹得非常詳細(xì),尤其是對(duì) AHB 方式讀支持的實(shí)現(xiàn),現(xiàn)在痞子衡再介紹下 FlexSPI 對(duì)于寫(xiě)時(shí)序的支持。第一節(jié)里介紹的 Flash 寫(xiě)操作的三個(gè)子時(shí)序,在 FlexSPI 外設(shè)里當(dāng)然都是支持的,SEQ_CTL 組件里都預(yù)先實(shí)現(xiàn)了這些子時(shí)序,比如下面就是 Page Program 的序列:
因?yàn)?Flash 寫(xiě)操作需要三個(gè)子序列,比 Flash 讀操作單序列要復(fù)雜得多,并且最關(guān)鍵的是寫(xiě)操作還包含一個(gè)不確定的等待周期(Read Status 子時(shí)序與 Flash 交互),這就導(dǎo)致 FlexSPI 外設(shè)在 AHB 方式寫(xiě)上沒(méi)法完美支持,這也是為什么寫(xiě)入 Flash 都是通過(guò) IPG 方式來(lái)完成的,因?yàn)?IPG 方式下,子序列可以隨意組合,由用戶(hù)代碼手動(dòng)調(diào)度。原則上三個(gè)寫(xiě)操作子序列可以放在 LUT 中的任何一個(gè) Sequence 位置,因?yàn)榧词拱葱蚍旁谝黄穑覀兺ㄟ^(guò) FlexSPI->FLSHxCR2 寄存器(x可取A1/A2/B1/B2,具體根據(jù)Flash引腳連接來(lái)定)中的 AWRSEQID 位指明寫(xiě)操作第一個(gè)子序列在 LUT 中的位置(index) 也無(wú)法自動(dòng)完成 Page 數(shù)據(jù)的完整寫(xiě)入操作。但也不要就此放棄,單獨(dú) Page Program 子序列還是可以通過(guò) AHB 方式寫(xiě)來(lái)替代的,這樣也可以讓我們過(guò)一下 AHB 方式寫(xiě)入 Flash 的癮,只是需要在 AHB 寫(xiě)入操作前后輔助 IPG 方式下的 Write Enable 和 Read Status 動(dòng)作,下一節(jié)用代碼給大家實(shí)際演示。
三、FlexSPI driver用法例程路徑:\SDK_2.10.0_MIMXRT1170-EVK\boards\evkmimxrt1170\driver_examples\flexspi\nor\polling_transfer\cm7\iar 3.1 初始化先來(lái)看一下 FlexSPI 初始化函數(shù) flexspi_nor_flash_init(),這個(gè)函數(shù)需要三個(gè)配置變量:分別是 flexspi_config_t 型面向 FlexSPI 外設(shè)層的配置 flexspiconfig,flexspi_device_config_t 型面向 Flash 器件端的配置 deviceconfig,以及很核心的 customLUT(這里只列出了跟 Flash
讀寫(xiě) 操作相關(guān)的時(shí)序)。
#define ?NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD?????0 #define ?NOR_CMD_LUT_SEQ_IDX_WRITEENABLE????????2 #define ?NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD???4 #define ?NOR_CMD_LUT_SEQ_IDX_READSTATUSREG??????12 #define ?CUSTOM_LUT_LENGTH????????60 const ?uint32_t ?customLUT[CUSTOM_LUT_LENGTH]?=?{ ????/*?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 ), ????/*?Write?Enable?*/ ????[4 ?*?NOR_CMD_LUT_SEQ_IDX_WRITEENABLE]?= ????????FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,?kFLEXSPI_1PAD,?0x06 ,?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?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 ), };flexspi_device_config_t ?deviceconfig?=?{ ????.flexspiRootClk???????=?12000000 , ????.flashSize????????????=?0x4000 ,?/*?16Mb/KByte?*/ ????.CSIntervalUnit???????=?kFLEXSPI_CsIntervalUnit1SckCycle, ????.CSInterval???????????=?2 , ????.CSHoldTime???????????=?3 , ????.CSSetupTime??????????=?3 , ????.dataValidTime????????=?0 , ????.columnspace??????????=?0 , ????.enableWordAddress????=?0 , ????.AWRSeqIndex??????????=?0 , ????.AWRSeqNumber?????????=?0 , ????//?支持?AHB?讀的關(guān)鍵配置 ????.ARDSeqIndex??????????=?NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD, ????.ARDSeqNumber?????????=?1 , ????.AHBWriteWaitUnit?????=?kFLEXSPI_AhbWriteWaitUnit2AhbCycle, ????.AHBWriteWaitInterval?=?0 , };void ?flexspi_nor_flash_init (FLEXSPI_Type?*base) { ????CLOCK_SetRootClockDiv(kCLOCK_Root_Flexspi1,?2 ); ????CLOCK_SetRootClockMux(kCLOCK_Root_Flexspi1,?0 ); ????/*Get?FLEXSPI?default?settings?and?configure?the?flexspi.?*/ ????flexspi_config_t ?flexspiconfig; ????FLEXSPI_GetDefaultConfig(