大家好,我是痞子衡,是正經(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(