大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是i.MXRT下改造FlexSPI driver以AHB方式去寫入NOR Flash。
痞子衡前段時間寫過一篇 《串行NAND Flash的兩大特性導緻其在i.MXRT FlexSPI下無法XiP》,文章裡介紹了 NAND Flash 的 Page Read 等待特性(發完 Read 指令後需要回讀 Flash 内部狀态寄存器 Busy 位來判斷 Page 資料是否已準備好)導緻其無法像 NOR Flash 那樣通過 AHB 方式被便捷通路,僅能在一個 Page 空間裡實作 AHB 讀(前提是在 IPG 方式發完讀指令以及讀完狀态寄存器確定資料已經準備好後)。
回到 NOR Flash 上,我們可以輕松通過 AHB 方式讀取 Flash 資料,但寫入 Flash 一般都是調用 FlexSPI 驅動來實作(即 IPG 方式),那麼有沒有可能也通過 “AHB 方式來寫入 Flash” 呢?答案是可以的,但為啥痞子衡加了個引号,且往下看:
芯成 IS25WP128 是一顆很典型的四線 QSPI NOR Flash,其寫入(程式設計)時序是符合 JEDEC216 标準的。簡單來說,一個完整的寫時序包含三個獨立子時序:Write Enable 時序 + Page Program 時序 + Read Status 時序。
先來看打頭陣的 Write Enable 子時序,NOR Flash 内部的狀态寄存器會有一個位叫 WEL (Write Enable Latch),這個位控制着 Flash 的擦寫權限,預設值是 0(即不允許擦寫)。如果想要寫入 Flash,必須先通過 Write Enable 指令将 WEL 位臨時設為 1(這個位會随着目前的擦寫指令結束後自動恢複到 0)。

置位了 WEL 後,便可以傳輸 Page 資料給 Flash,這個子時序便是 Page Program。Page Program 按指令位址和資料傳輸方式不同分為三種:一線 SPI,四線 SPI,QPI,下面是常用的四線 SPI 的時序圖,指令和位址通過 IO0 傳輸,資料通過 IO[3:0] 傳輸。
通常來說,在這個時序裡,傳入的位址應該是 Page 首位址,寫入資料長度應該是一個完整的 Page 大小。但從非 Page 首位址處寫入小于一個 Page 長度的資料也是可以的,但有一個注意點就是不要在這個時序裡出現跨頁的現象(如果出現,超出目前頁的資料會被放回到該頁起始位址處)。
Page Program 子時序結束後,資料還并未真正寫入 Flash 記憶體體中,Flash 内部控制器隻是開始處理資料,這時候會有一個等待時間(大概0.2ms),Flash 内部的狀态寄存器有一個位叫 WIP (Write In Progress),這個位标志着資料寫入狀态(預設值是 0,當 Page Program 子時序結束後,WIP 立即跳為 1),使用者需要通過 Read Status 子時序來實時讀取狀态寄存器的值進而獲知資料處理情況。
當 Flash 内部狀态寄存器中的 WIP 位從 1 跳回到 0,便意味着一次完整的寫時序結束了,主機可以發起下一次寫時序。
痞子衡舊文 《從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable》 裡對 FlexSPI 讀時序介紹得非常詳細,尤其是對 AHB 方式讀支援的實作,現在痞子衡再介紹下 FlexSPI 對于寫時序的支援。
第一節裡介紹的 Flash 寫操作的三個子時序,在 FlexSPI 外設裡當然都是支援的,SEQ_CTL 元件裡都預先實作了這些子時序,比如下面就是 Page Program 的序列:
因為 Flash 寫操作需要三個子序列,比 Flash 讀操作單序列要複雜得多,并且最關鍵的是寫操作還包含一個不确定的等待周期(Read Status 子時序與 Flash 互動),這就導緻 FlexSPI 外設在 AHB 方式寫上沒法完美支援,這也是為什麼寫入 Flash 都是通過 IPG 方式來完成的,因為 IPG 方式下,子序列可以随意組合,由使用者代碼手動排程。
原則上三個寫操作子序列可以放在 LUT 中的任何一個 Sequence 位置,因為即使按序放在一起,我們通過 FlexSPI->FLSHxCR2 寄存器(x可取A1/A2/B1/B2,具體根據Flash引腳連接配接來定)中的 AWRSEQID 位指明寫操作第一個子序列在 LUT 中的位置(index) 也無法自動完成 Page 資料的完整寫入操作。
但也不要就此放棄,單獨 Page Program 子序列還是可以通過 AHB 方式寫來替代的,這樣也可以讓我們過一下 AHB 方式寫入 Flash 的瘾,隻是需要在 AHB 寫入操作前後輔助 IPG 方式下的 Write Enable 和 Read Status 動作,下一節用代碼給大家實際示範。
先來看一下 FlexSPI 初始化函數 flexspi_nor_flash_init(),這個函數需要三個配置變量:分别是 flexspi_config_t 型面向 FlexSPI 外設層的配置 flexspiconfig,flexspi_device_config_t 型面向 Flash 器件端的配置 deviceconfig,以及很核心的 customLUT(這裡隻列出了跟 Flash 讀寫操作相關的時序)。
先來看 IPG 方式的 Flash 寫入函數,其中 Page Program 子時序是通過 FLEXSPI_TransferBlocking() 函數來完成的,這個函數就是往大小為 256 bytes 的 IP TX FIFO 寫 src 裡的資料(預設 FlexSPI->MCR0[ATDFEN] = 0 情況下),SEQ_CTL 元件處理時會将緩存在 IP TX FIFO 裡的資料全部發送到 Flash 端。
我們現在來改造 IPG 方式的 Flash 寫入函數,首先要修改 deviceconfig 變量将 AWRSeqIndex 指向 PAGEPROGRAM_QUAD 在 LUT 中的位置,然後将 FLEXSPI_TransferBlocking() 函數換成 AHB 寫入代碼(memcpy 或者指針操作指派),這時候 src 裡的資料就會被自動放在大小為 64 bytes 的 AHB TX Buffer 裡,SEQ_CTL 元件處理時會将緩存在 AHB TX Buffer 裡的資料全部發送到 Flash 端。
但這裡有一些限制,經實測,利用 memcpy 做 AHB 寫,一次僅能寫入 1/2/3/4/8 這五種有效長度的資料,其他資料長度不及預期(比如拷貝 5 - 7 位元組,實際僅寫入前 4 位元組;拷貝 8 位元組以上,實際僅寫入前 8 位元組),這其實跟 《i.MXRT中FlexSPI外設對AHB Burst Read特性的支援》 一文裡提及的處理器 AHB Burst 政策有關,FlexSPI 每次僅會緩存一次 AHB Burst 寫資料進 AHB TX Buffer,而 SEQ_CTL 每工作一次都會使能一次 Flash 器件的資料處理流程(進入 Busy 狀态),是以連續的兩次 AHB burst 寫,後面一次的 burst 行為其實不産生實際 Flash 寫入效果。
上面看似雞肋的 AHB 方式寫入 Flash 到底有什麼用?底下痞子衡會講到 XECC 子產品,到時你就知道其用處了。
至此,i.MXRT下改造FlexSPI driver以AHB方式去寫入NOR Flash痞子衡便介紹完畢了,掌聲在哪裡~~~
文章會同時釋出到我的 部落格園首頁、CSDN首頁、知乎首頁、微信公衆号 平台上。
微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。
最後歡迎關注痞子衡個人微信公衆号【痞子衡嵌入式】,一個專注嵌入式技術的公衆号,跟着痞子衡一起玩轉嵌入式。
衡傑(痞子衡),目前就職于恩智浦MCU系統部門,擔任嵌入式系統應用工程師。
專欄内所有文章的轉載請注明出處:http://www.cnblogs.com/henjay724/
與痞子衡進一步交流或咨詢業務合作請發郵件至 [email protected]
可以關注痞子衡的Github首頁 https://github.com/JayHeng,有很多好玩的嵌入式項目。
關于專欄文章有任何疑問請直接在部落格下面留言,痞子衡會及時回複免費(劃重點)答疑。
痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。