天天看點

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是i.MXRT啟動頭FDCB裡的lookupTable。

  一個MCU内部通常有很多外設子產品,這些外設子產品是各MCU廠商做差異化産品的本質,也是各廠商核心競争力所在(這裡特指那些生産ARM Cortex-M核心MCU的廠商)。在做MCU開發時有時候并不需要了解全部的外設,因為有些外設在項目裡不一定會用到,但是要想把恩智浦i.MXRT系列MCU玩起來,有一個外設是必須要有所了解的,它就是FlexSPI,這個外設負責與外部串行NOR Flash連接配接,實作外部NOR Flash裡的應用程式指令與資料的讀取,而串行NOR Flash正是i.MXRT首選的啟動裝置。

  那麼在FlexSPI外設子產品裡究竟是什麼機制實作了Flash中應用程式指令與資料的讀取功能呢?痞子衡從i.MXRT啟動頭FDCB裡的lookupTable設定開始說起:

  關于在串行NOR Flash XIP執行原理,痞子衡其實在之前一篇文章 《在串行NOR Flash XIP調試原理》 的第二小節 i.MXRT FlexSPI外設特性 介紹過,是FlexSPI這個外設實作了從串行Flash任意位址取指令的功能,這是先決條件。

  有了從Flash任意位址取指的先決條件基礎,在i.MXRT晶片上電後,BootROM便隻需要将FlexSPI外設配置到指定工作狀态(這裡詳見 《深入i.MXRT1050系列ROM中串行NOR Flash啟動初始化流程》 一文,尤其是文中最後一節提到的第二次FlexSPI初始化,本文讨論的内容其實屬于第二次初始化後的狀态),FlexSPI外設配置資訊完全來自于啟動頭FDCB(一共512bytes),FlexSPI配置完成後,BootROM再把CPU控制權交給應用程式,這就完成了啟動任務。

  下面的 qspiflash_config 便是i.MXRT SDK包裡使用的一個典型的适用符合JEDEC SFDP标準且容量為8MB的QSPI NOR Flash的FDCB頭。這個啟動頭将FlexSPI配置成了四線模式,100MHz時鐘頻率,Quad I/O Fast Read時序模式(注意這個頭裡lookupTable設定寫法其實并不标準,沒有顯式地寫出模式序列和停止序列,後面痞子衡會細說):

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  當PC開始指向FlexSPI映射空間(0x60000000 - 0x607FFFFF)去執行使用者程式時,FlexSPI便在背後一直默默為CPU送上指定的指令資料,如下圖綠色箭頭流向所示。指令資料從外部Flash中通過IO_CTL且按照SEQ_CTL指定的時序送入RX_FIFO,再到AHB_RX_BUF,最後經過AHB_CTL送到系統AHB總線上,以被CPU無障礙擷取。整個過程中最重要的自動化環節其實是黃色框内的SEQ_CTL,是這個SEQ_CTL在時刻驅動着FlexSPI發送符合Flash要求的讀通路時序。

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  經過上一節的分析,我們知道了是FlexSPI中的SEQ_CTL元件實作了核心的Flash通路時序控制,那麼SEQ_CTL我們該怎麼控制它?别急,這時候該LUT登場,LUT是Look Up Table的簡稱,它其實是FlexSPI内部的一塊存儲區(即FlexSPI->LUTx寄存器),它的組織結構如下,LUT由多個Sequence組成(比如i.MXRT1050上是16個),每個Sequence由最多8個instruction組成,每個instruction大小為16bits,分為opcode(序列編号) + num_pads(管腳模式) + operand(序列參數值)三部分。

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  每個instruction,你可以了解為一個Flash通路傳輸子序列(比如指令序列、位址序列、模式序列,dummy序列,讀/寫資料序列,停止序列等),在FlexSPI外設子產品裡面預先實作了很多個基礎instruction,instruction中的opcode即是那些預實作的序列編号。opcode全部編号如下:

  有了這些基礎instruction,我們便可以自由組合它們(最多8個),得到我們想要的完整傳輸Sequence。比如最常見的Quad I/O Read SDR傳輸時序便由CMD_SDR + RADDR_SDR + MODE8_SDR + DUMMY_SDR + READ_SDR + STOP六個子序列組成,如下表所示:

Note: 關于READ_SDR的參數值設定(即讀取資料長度)需要特别說明一下,這個參數僅對IP CMD方式的通路時序有效;而對于AHB CMD方式的通路時序,這個參數值設定是無效的,實際讀取資料長度是由AHB RX Buffer政策靈活決定的。
痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  從引腳信号上來看,完整Quad I/O Read SDR傳輸時序如下圖所示。注意有一處要特别說明,從FlexSPI外設本身而言,MODE8_SDR序列和DUMMY_SDR序列是互相獨立的,但在不少Flash晶片上,MODE8_SDR所占的2個時鐘周期也被算在了總Dummy時鐘周期數裡。

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  LUT中最多可以存儲16個Sequence,對于XIP執行而言,隻需要一個讀通路時序(比如最常用的Quad I/O Read SDR傳輸時序)即可。如果是IAP,那麼還需要添加擦除時序,寫通路時序,寫使能時序,讀狀态寄存器時序等。這些預先存放在LUT中的Sequence被使用者按需觸發以實作各種不同類型的Flash通路,這就是SEQ_CTL工作機制。

  從FlexSPI外設子產品設計上而言,LUT裡16個Sequence地位是相同的,對于XIP執行,必要的讀通路時序可以放在LUT中的任何一個Sequence位置,隻需要在FlexSPI->FLSHxCR2寄存器(x可取A1/A2/B1/B2,具體根據Flash引腳連接配接來定)中的ARDSEQID位指明讀通路時序在LUT中的位置(index)即可。

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  但是畢竟應用程式是由BootROM引導的,BootROM有自己的一套配置FlexSPI規則,它定死了CMD_LUT_SEQ_IDX_READ位置,即讀通路時序必須是FlexSPI->LUT[]中第一個Sequence,因為FlexSPI->FLSHxCR2[ARDSEQID]被BootROM配置成了0。是以我們在準備FDCB時,lookupTable中第一個Sequence必須放置讀通路時序。

  再來看BootROM中的FlexSPI初始化函數,在外設子產品基本初始化 flexspi_init() 完成後,然後 flexspi_update_lut() 被調用去更新了一次LUT就直接結束了。這次的LUT更新其實僅僅是将FDCB裡的lookupTable[0] - lookupTable[3](第一條Sequence) 填到 FlexSPI->LUT[0] - FlexSPI->LUT[3]裡。至于為何有時候你會看到FDCB裡lookupTable中不止一條Sequence,這個痞子衡後面另有文章再聊。

  我們以i.MXRT官方EVK上配套的典型Flash型号IS25WP064AJBLE來實戰,下圖是該Flash的Fast Read Quad I/O Sequence,這個時序圖中指令序列、位址序列、Dummy序列的參數值是明确的,但模式序列、讀資料序列參數值并不明确,我們給它明确一下,模式序列中mode bits我們設為0x00(其實隻要不是0xAx均可),即 non-continuous read mode;讀資料序列中data out byte其實不可設(上面講過AHB通路下是由RX Buffer政策自動控制的),随便寫個非0值即可。

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  基于上面的真實Flash讀資料傳輸時序圖,我們在FDCB中lookupTable裡的對應設定應如下:

  當我們放好了正确的FDCB,BootROM正常配置完FlexSPI,并啟動了應用程式後,CPU便開始按部就班從FlexSPI映射區域直接AHB通路去擷取應用程式指令,是不是每一次的CPU通路都會讓SEQ_CTL元件按LUT裡的設定發送一次讀通路時序呢?其實并不是!

  我們知道i.MXRT系列會有L1 Cache,如果Flash某位址裡的指令内容緩存在L1 Cache裡,那麼目前CPU通路該Flash位址處的指令并不需要從Flash裡重新再擷取一次,CPU直接從cache裡便可以得到指令,此時SEQ_CTL不會工作。

  即便L1 Cache裡沒有緩存到CPU所要指令,如果FlexSPI本身的Cacheable和Prefetch功能打開的話,AHB RX/TX Buffer裡可能也會緩存CPU所要指令。如果所需指令确實緩存在AHB Buffer裡,SEQ_CTL仍然不會工作。

  僅當CPU所要指令是全新的,完全沒有緩存,SEQ_CTL才會真正開始工作,按LUT設定去發送讀資料通路時序給Flash。

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  前面講了,我們在lookupTable裡無法有效設定讀資料序列中data out byte,因為AHB通路下的一次讀取的長度是由RX Buffer政策控制的。在i.MXRT1050中AHB RX Buffer總大小為1KB,分為四個:AHB RX Buffer0 - AHB RX Buffer3,每個Buffer的大小都是可配的。具體配置在如下FlexSPI->AHBRXBUFxCR0寄存器裡:

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  BootROM使用了如下 flexspi_config_ahb_buffers() 函數配置了AHB Buffer,即開啟了FlexSPI的Prefetch功能,并且将前三個FlexSPI->AHBRXBUFxCR0[BUFSZ]全部設為了0(保留了FlexSPI->AHBRXBUF3CR0[BUFSZ]的預設配置,實際上這種情況等效于将四個RX Buffer全部置0),根據手冊,這種配置意味着僅啟用Buffer3作為唯一的RX Buffer(當Buffer3是唯一Buffer時,其BUFSZ設定是無效的,其真實大小永遠等于總Buffer大小),并且Buffer3大小為1KB。那麼我們現在知道了,在Prefetch開啟的情況下,SEQ_CTL工作一次就會讀取1KB資料。當然Prefetch功能是可以在應用程式裡被關掉的,如果Prefetch不使能,SEQ_CTL工作一次擷取的資料由AHB Burst Read政策決定(關于這個痞子衡會單獨寫一篇文章)。

  至此,i.MXRT啟動頭FDCB裡的lookupTable痞子衡便介紹完畢了,掌聲在哪裡~~~

文章會同時釋出到我的 部落格園首頁、CSDN首頁、知乎首頁、微信公衆号 平台上。

微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  最後歡迎關注痞子衡個人微信公衆号【痞子衡嵌入式】,一個專注嵌入式技術的公衆号,跟着痞子衡一起玩轉嵌入式。

痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable
痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable
痞子衡嵌入式:從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable

  衡傑(痞子衡),目前就職于恩智浦MCU系統部門,擔任嵌入式系統應用工程師。

  專欄内所有文章的轉載請注明出處:http://www.cnblogs.com/henjay724/

  與痞子衡進一步交流或咨詢業務合作請發郵件至 [email protected]

  可以關注痞子衡的Github首頁 https://github.com/JayHeng,有很多好玩的嵌入式項目。

  關于專欄文章有任何疑問請直接在部落格下面留言,痞子衡會及時回複免費(劃重點)答疑。

  痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。