你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們分享的是i.MXRT上使用16MB以上NOR Flash軟復位沒法正常啓動問題的分析解決經驗。緩存
痞子衡這幾天在支持一個i.MXRT1050客戶項目,客戶遇到了軟復位沒法從32MB NOR Flash從新啓動的問題。這個客戶是作醫療設備的,已經基於i.MXRT作出一款成功的產品了,因此客戶其實有豐富的i.MXRT使用經驗。目前調試的項目是客戶的第二款產品,這個軟復位沒法啓動問題已經困擾他們好久,但問題畢竟不是特別緊急,不影響他們開發進度,因此耽擱至今。此次客戶趁着出差蘇州參加勞特巴赫TRACE32調試器培訓機會,讓痞子衡現場幫他們定位問題,通過一番調試和分析,痞子衡終於成功地解決了問題,特此將問題解決的全過程記錄下來,供你們參考。微信
在描述問題前,首先給你們介紹下客戶的項目設計,底下是客戶硬件簡圖。客戶選用的i.MXRT1052做爲主控,掛載了兩個QSPI Flash,FlexSPI接口鏈接的32MB Flash用於啓動和存放靜態圖片資源(只須要讀便可),LPSPI接口鏈接的1MB Flash用於存放運行時狀態數據(須要讀寫),此外板子鏈接了一個顯示屏,因此還掛載一片SDRAM用於顯示緩存,其實SDRAM除了顯示緩存功能以外,還用於執行App(QSPI Flash裏的App會自加載到SDRAM執行)。app
有必要重點介紹下QSPI Flash啓動設計細節,客戶選用的Flash型號是ISSI的IS25WP256D,這是一款容量256Mb的四線串行Flash。客戶啓動流程設計的挺複雜,芯片上電以後,BootROM負責從Flash中XIP啓動L2 loader程序,L2 loader運行後從Flash中選出最新的一份Boot程序(A/B是雙備份),將其加載到SDRAM中執行。Boot程序運行後作一些系統初始化工做,而後直接跳轉到App中執行(XIP),App纔是最終的客戶應用程序,這個應用程序會完成往SDRAM的自拷貝以及跳轉執行。函數
客戶的App實際大小接近5MB,對於嵌入式程序來講,這個體量至關大了,這也是爲何客戶須要藉助專業的勞特巴赫TRACE32調試器來分析定位程序邏輯設計問題。從下圖還能夠看到從0x60800000開始,Flash中還存放了一些靜態圖片資源,客戶項目有顯示屏,Flash裏放一些固定圖片數據方便UI切換。工具
介紹完客戶的項目設計,如今描述客戶的軟復位沒法從新啓動問題。其實這個問題現象很簡單,就是每次從新上電啓動,程序都是能夠正常運行的,可是一旦使用按鍵軟復位(ONOFF Reset),系統就會有必定機率起不來(機率很大,很容易復現),調試器連上去會發現PC停留在BootROM裏,這意味着此時BootROM沒能正常啓動L2 loader。測試
讓咱們來分析一下問題,這個問題要從兩方面來考慮:1、板子上芯片的POR和軟復位的區別;2、軟復位沒法啓動是機率性的,所以痞子衡想到了以下四處疑點:flex
- 兩種復位下主芯片內部非易失寄存器狀態的區別是否對BootROM運行產生了影響?
- 兩種復位下主芯片內FlexSPI這個模塊狀態是否有區別?
- 兩種復位下外掛Flash芯片狀態是否有區別?
- 客戶App代碼裏是否有某種操做致使了機率性問題的發生?
由於每次都是軟復位從新啓動出問題,因此客戶板級供電設計不在疑點範圍內。雖然問題都表如今BootROM無法加載L2 loader執行,但BootROM自己缺陷也不是咱們主要考慮的方向,畢竟BootROM是固化在芯片內部的,可靠性有必定保證。咱們首先要把疑點放在機率性以及兩種復位的差別上,那麼咱們從哪裏開始着手測試?ui
痞子衡想的是先從第4個疑點開始下手,緣由是前3個疑點本質上都由第4個疑點引發的,客戶代碼的執行可能會改主芯片內部非易失性寄存器,也同時會操做FlexSPI模塊去訪問外部Flash,它是問題的引爆點。.net
i.MXRT內部有一些非易失性寄存器(好比IOMUXC_GPR寄存器組,SRC寄存器等),這些寄存器僅在POR時纔會被複位,而普通軟復位是不會改變其狀態的。客戶App代碼近5MB,若是是去肉眼排查是否操做了非易失性寄存器,不免有疏漏。最簡單的方法就是在正常啓動和非正常啓動時分別用調試器將這些寄存器的值所有保存下來,而後使用文本工具去對比。經測試,兩種狀況下,這些非易失性寄存器並沒有區別,所以這個疑點被排除。設計
如今咱們開始逐步精簡App代碼,因爲客戶代碼涉及機密,因此精簡的工做由客戶來作,固然客戶也最清楚如何去精簡他們本身的代碼。一番測試下來,咱們發現App代碼裏只要不去讀存在Flash裏的靜態圖片數據,就不會存在軟復位沒法從新啓動問題,看起來咱們已經找到線索了。
問題出在App代碼裏讀存在Flash裏的靜態圖片數據,這意味着App裏可能用了特殊的讀Flash方法改變了Flash狀態,而且這個Flash狀態是非易失性的。謎團接近解開了,痞子衡讓客戶公佈了他們的L2 loader裏的FDCB配置頭以及App裏的讀Flash圖片的代碼實現:
先來看客戶的FDCB啓動頭,客戶僅讓BootROM配置Flash工做於50MHz,而且是1bit SDR Fast Read(命令是0x0B),這是標準3字節地址讀,所以配置成功後經過AHB總線最大可訪問16MB之內的Flash空間。由於客戶的L2 loader很小,且存儲在Flash的起始地址,因此這樣的配置對於啓動而言沒有問題。
再來看客戶實現的讀Flash函數BigCapRead(),根據前面的介紹,靜態圖片數據是從0x60800000處開始存儲的,所以0x60800000 - 0x60FFFFFF範圍內的8MB數據是能夠直接AHB讀,可是0x61000000地址以後的數據在上述BootROM的配置下沒法直接訪問,這也是爲何客戶寫了BigCapRead()函數,這個函數會根據傳入的地址範圍來判斷數據是在低16MB空間仍是高16MB空間,而後對地址空間作了一個切換。
#define FLASH_BIG_CAP_SIZE (0x1000000) static status_t flexspi_nor_select_segment(uint32_t base, uint8_t seg) { qspi_transfer_t flashXfer; status_t status = Success; uint32_t writeValue = 0x00; uint32_t readValue = 0x00; flexspi_nor_write_enable(base, 0, true); flexspi_nor_read_volatilebankaddr_reg(base, &readValue); if ((readValue & 0x01) == (seg & 0x1)) { return Success; } writeValue = seg & 0x1; flexspi_nor_write_volatilebankaddr_reg(base, writeValue); flexspi_nor_read_volatilebankaddr_reg(base, &readValue); if (readValue != writeValue) { return Failure; } flexspi_nor_wait_bus_busy(base); return Success; } static UINT32 BigCapRead(struct flash_dev* dev, UINT32 start_addr, UCHAR *buffer, UINT32 size) { UINT32 tempLen = 0; UINT32 result = 0; if (start_addr >= FLASH_BIG_CAP_SIZE) { flexspi_nor_select_segment(dev->base, 1); start_addr = start_addr - FLASH_BIG_CAP_SIZE; result = Read(dev, start_addr, buffer, size); } else { if (start_addr + size < FLASH_BIG_CAP_SIZE) { flexspi_nor_select_segment(dev->base, 0); result = Read(dev, start_addr, buffer, size); } else { tempLen = FLASH_BIG_CAP_SIZE - start_addr; flexspi_nor_select_segment(dev->base, 0); result = Read(dev, start_addr, buffer, tempLen); flexspi_nor_select_segment(dev->base, 1); result = Read(dev, 0, buffer + tempLen, size - tempLen); } } return result; }
對於16MB以上空間的Flash,總會面臨3/4字節地址訪問的問題,JESD216規定了3/4字節地址訪問標準命令,對於3字節地址Fast Read,其命令是FRD(0x0B);而對於四字節地址Fast Read,其命令是4FRD(0x0C)。對於一個32MB的Flash,若是僅須要訪問低16MB空間,可使用FRD;若是須要訪問高16MB空間,則須要使用4FRD。
4FRD相比FRD多傳輸了一字節地址,對於地址連續的大塊數據訪問,這個1字節地址影響不太,可是若是是執行代碼或者非連續數據訪問,4FRD相比FRD仍是有效率上的下降的,對於這個問題,不一樣的廠家提供了不一樣的解決方案。
客戶選用的這款Flash來自ISSI,ISSI的解決方案是在Flash內部增長一個Bank Address Register,其bit0用於實時切換低Bank和高Bank(而且這個位是非易失性的,僅POR纔會復位,引腳reset沒法復位!),當bit0值爲0時,FRD命令訪問的是低16MB空間,而bit0置1後,FRD命令此時實際訪問的是高16MB空間(從AHB地址上看不出這個變化)。
看到這,這個軟復位沒法重啓問題真相大白了,是因爲這顆Flash裏的內部非易失寄存器BAR[0]的操做致使的,若是軟復位時間點剛好在App讀了高16MB空間(Bank1)裏的數據以後,此時Bank發生了切換,軟復位後BootROM去啓動時沒法讀到存在低16MB空間(Bank0)的有效的L2 loader。若是軟復位時間點發生在App正在讀低16MB空間的數據,那下次仍是能夠正常啓動,這就是機率性啓動失敗的緣由。
緣由調查清楚了,問題解決方法就很簡單了,將L2 loader裏的FDCB頭用4FRD代替FRD,這樣BootROM配置完成以後,能夠直接AHB讀所有的32MB空間,不須要切換Bank,所以App裏的BigCapRead()函數裏的Bank切換操做能夠刪掉。
這個經驗也告訴了咱們,當使用16MB以上Flash做爲啓動設備時,必定要當心處理好3/4字節地址訪問問題,否則就可能出現啓動問題。
至此,i.MXRT上使用16MB以上NOR Flash軟復位沒法正常啓動問題的分析解決經驗痞子衡便介紹完畢了,掌聲在哪裏~~~
文章會同時發佈到個人 博客園主頁、CSDN主頁、知乎主頁、微信公衆號 平臺上。
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就能夠在手機上第一時間看了哦。