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