你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們介紹的是飛思卡爾i.MX RTyyyy系列MCU的Raw NAND啓動。html
前面鋪墊了七篇啓動系列文章,終於該講具體Boot Device了,咱們知道i.MXRTyyyy支持的外部Boot Device共有6種(Serial NOR&NAND、Parallel NOR&NAND、SD/eMMC、SPI NOR/EEPROM),其中最經常使用的是Serial NOR&NAND,目前各大社區裏討論最火的也是Serial NOR/NAND啓動,有很多大神(硬漢eric2013, jicheng0622)已經寫過關於Serial NOR&NAND啓動的文章,寫得很是好,這讓痞子衡很是有壓力,所以痞子衡決定第一篇Boot Device寫較經常使用但尚未人寫過的Raw(Parallel) NAND啓動,你們是否是很期待?(請配合說「是」),好,話很少說,開講。緩存
開門見山,i.MXRT支持加載啓動的主要是兼容ONFI 1.0標準的Asynchronous SLC Raw NAND,至於數據線寬度,x8,x16都支持(通常x8應用比較多)。關於Raw NAND基本知識請先看一下痞子衡的另外一篇文章 並行接口NAND標準(ONFI)及SLC Raw NAND簡介,本文後續的不少內容均是基於充分了解Raw NAND的前提下開展的。數據結構
Note1: ONFI是最流行的NAND標準,ONFI 1.0僅針對50MB/s低速Async SDR模式NAND,從ONFI 2.x開始逐步引入133MB/s、166MB/s、200MB/s高速Sync模式NAND的支持,從ONFI 3.x開始又引入400MB/s、533MT/s(NV-DDR2)更高速NAND的支持,從ONFI 4.x開始更是加入了667MT/s、800MT/s、1066MT/s、1200MT/s(NV-DDR3)超高速NAND的支持。
Note2: 關於NAND還有一個JEDEC標準JESD230,該標準是JEDEC與ONFI組織合做制定的,主要是定義了Async SDR、Sync DDR, Toggle DDR模式NAND的互操做性。JESD230標準版本可與ONFI 3.1及其以後的版本相對應。app
Raw NAND廠商很是多,對應Raw NAND芯片型號也不少,若是你在選型時不肯定到底該爲i.MXRT選擇哪一款Raw NAND時,可選用下面五款芯片,痞子衡均實測過:ide
Macronix MX30LF4GE8AB-TI (x8 bits, 2KB Page/128KB Block/4Gb Device, 0bit ECC, 3.3V) Micron MT29F4G08ABBDAWP (x8 bits, 2KB Page/128KB Block/4Gb Device, 4bit ECC, 1.8V) Micron MT29F4G08ABAFAWP:D (x8 bits, 2KB Page/128KB Block/4Gb Device, 4bit ECC, 3.3V) Micron MT29F16G08ABACAWP:C (x8 bits, 4KB Page/512KB Block/16Gb Device, 4bit ECC, 3.3V) Winbond W29N01GVSIAA (x8 bits, 2KB Page/128KB Block/1Gb Device, 1bit ECC, 3.3V)
肯定了Raw NAND芯片選型後,底下便進入Raw NAND硬件電路設計及與i.MXRT的信號鏈接環節:工具
i.MXRT對於Raw NAND的底層接口支持是經過內部SEMC這個IP實現的,以下是SEMC的內部模塊圖,從圖中咱們能夠看到,從內部數據總線來看SEMC支持AXI Bus/IP Bus兩種(此兩種方式會在後續eFUSE配置裏看到),而從外部接口來看SEMC最多能支持五種設備(SDRAM, NAND, NOR, SRAM, 8080 Display),NAND是其中一種。ui
雖然SEMC最多能支持五種設備,但並非同時支持的,同一時刻僅能支持一種設備,所以SEMC接口信號必然是複用的,下表是SEMC接口複用表,關於NAND接口信號,須要特別說一下的是CE#信號,從表中咱們能夠看到NAND的CE6#信號有5個,即有5種配置選擇,但i.MXRT BootROM固定選擇的是SEMC_CSX[0],這點在設計NAND硬件鏈接時須要特別注意。設計
以下是典型的NAND硬件鏈接設計,示例NAND芯片是MX30LF4GE8AB-TI(經典的TSOP-48封裝,芯片絲印上的L代表其是3.3V供電),其中WP#信號沒有使能,而且供電選擇同時支持3.3V和1.8V(經過R302選擇,此處應連2-3),有朋友會疑問,爲何此處要留有2路不一樣供電電壓?由於後期方便咱們更換不一樣的供電輸入的NAND芯片。3d
確保Raw NAND硬件相關設計無誤以後,底下即是下載更新Bootable Image進Raw NAND以供BootROM加載啓動了,在下載Bootable image以前有必要先了解Raw NAND的加載啓動過程:調試
痞子衡在啓動系列文章的第六篇 Bootable image格式與加載(elftosb/.bd) 裏的最後已經介紹過non-XIP image加載啓動過程,但實際上那個過程對於存儲在外部NAND Flash中Bootable image而言仍是介紹得不夠全面,欠缺FCB/DBBT的處理流程,你確定會疑問FCB/DBBT是什麼?這得從NAND與NOR差別提及,咱們知道NOR Flash中全部空間都必須是可用的(即不容許有壞塊),這意味着NOR Flash中的Bootable image數據是能夠按指定地址連續存放的(即所謂的線性存儲),而且Application能夠原地XIP執行;可是NAND Flash中經常是有壞塊的(出廠壞塊,使用中產生壞塊),這就致使NAND可用空間地址不可預知而且有可能不連續,所以存儲在NAND中的Bootable image數據極有可能並非連續存放的,而且Bootable image實際存儲的起始地址也不必定就是指定的起始地址(即所謂的非線性存儲)。
舉例來講,若是NAND的block大小爲128KB,Firmware(即Bootable Image)大小爲260KB,咱們指定從NAND地址0x40000處(即block index = 2)開始存儲Firmware,可是很不幸的是index爲二、4的block均是壞塊,那麼實際上Firmware被分散存儲在了index爲三、五、6三個block中,爲了未來能正確地從NAND中讀回Firmware,咱們須要額外記錄至少兩個信息,一是指定的Firmware起始存儲地址0x40000,二是NAND中壞塊信息block index 二、4。FCB/DBBT就是用來記錄這些額外的信息。
FCB大小爲1KB,其主要記錄了Firmware信息(地址,長度,份數),以及DBBT地址信息。DBBT大小爲1056bytes,其記錄了NAND芯片中全部的壞塊個數以及位置,DBBT即所謂的壞塊表。FCB/DBBT最大可有兩份,實際應用中通常只用一份便可,後面介紹均以一份FCB/DBBT爲例講解,FCB0永遠從NAND地址0x0處(即index爲0的block中的第1個Page)開始存放,DBBT0通常放在index爲n的block裏(其實n是可設的,這在後面使用Flashloader時會講到,爲求簡單咱們經常設n=1),Firmware 0通常放在index爲n+1的block裏(Firmware只容許從index爲n+1的block及其以後開始存放,Firmware最大能夠有8份)。關於FCB/DBBT結構原型,後續會進一步介紹。
有了前面的背景知識,NAND的加載啓動過程即是上電以後,BootROM先從NAND起始地址處獲取FCB0數據,再根據FCB0裏的信息獲取DBBT0數據以及Firmware 0起始地址,底下便進入跟NOR Flash同樣的加載過程,只不過在加載Firmware 0的過程當中須要根據DBBT0壞塊表信息自動跳過壞塊。若是在讀取Firmware 0時,發現部分Firmware數據所在的block是一個壞塊,可是這個block沒有被記錄在DBBT0中,這說明該壞塊是新產生的(該新壞塊信息會在下一次下載Application時記錄在新DBBT中),存在該壞塊中的Firmware數據被破壞了,Firmware 0便失效了,BootROM便會嘗試按一樣的流程去加載Firmware 一、2...7,直到找到有效的Firmware,這就是爲何在NAND中存儲多份Firmware的意義。
理解了Raw NAND加載啓動過程,咱們即可以開始使用Flashloader下載Application進Raw NAND芯片中:
痞子衡在啓動系列文章的第四篇 Flashloader初體驗(blhost) 和第六篇 Bootable image格式與加載(elftosb/.bd) 裏分別介紹了Flashloader的基本使用以及如何將你的Application製做成Bootable image,後續內容假定你已經制做好一個Bootable image而且使用blhost工具與Flashloader創建了基本通訊,正要開始將Bootable image下載進Raw NAND。
前面講過Raw NAND中除了要有Bootable image(Firmware)以外,還須要有FCB/DBBT,而且FCB/DBBT在Raw NAND中存儲的位置是比Bootable image靠前的,所以你遇到的第一個問題即是如何下載FCB/DBBT進Raw NAND?
首先來看FCB和DBBT的原型,以下semc_nand_fcb_t是FCB原型,semc_nand_dbbt_t是DBBT原型:
FCB/DBBT結構體開頭都是12bytes的semc_bcb_header_t,這個bcb header由Tag、Version、CRC Checksum(CRC32-MPEG2)組成,用於驗證FCB/DBBT的完整性。
semc_nand_fcb_t.DBBTSerachAreaStartPage標明DBBT所在位置;semc_nand_fcb_t.searchStride和semc_nand_fcb_t.searchCount用於存在2份FCB/DBBT時標明第二份位置(此處咱們僅用一份,因此searchCount設爲1,searchStride的值不用管);semc_nand_fcb_t.firmwareCopies記錄Firmware總份數,semc_nand_fcb_t.firmwareTable標明全部Firmware具體位置;semc_nand_fcb_t.nandConfig是Raw NAND的configuration block,大小爲256bytes,記錄Raw NAND特性參數。
semc_nand_dbbt_t.badBlockNumber記錄壞塊總個數,semc_nand_dbbt_t.badBlockTable標明全部壞塊具體位置。
#define SEMC_NAND_BAD_BLOCKS_MAX_NUM 256 #define SEMC_NAND_FW_MAX_NUM 8 #define SEMC_NAND_FCB_TAG 0x4E464342U //!< ASCII: "NFCB" #define SEMC_NAND_FCB_VERSION 0x00000001 //!< Version: 1.0 #define SEMC_NAND_DBBT_TAG 0x44424254U //!< ASCII: "DBBT" #define SEMC_NAND_DBBT_VERSION 0x00000001 //!< Version: 1.0 typedef struct _nand_firmware_info { uint32_t startPage; uint32_t pagesInFirmware; } nand_firmware_info_t; typedef struct _semc_bcb_header { uint32_t crcChecksum; //!< [0x000-0x003] uint32_t fingerprint; //!< [0x004-0x007] uint32_t version; //!< [0x008-0x00b] } semc_bcb_header_t; typedef struct __semc_nand_config { semc_mem_config_t memConfig; //!< [0x000-0x04f] uint8_t vendorType; //!< [0x050-0x050] uint8_t cellTechnology; uint8_t onfiVersion; uint8_t acTimingTableIndex; uint8_t enableEccCheck; //!< [0x054-0x054] uint8_t eccCheckType; uint8_t deviceEccStatus; uint8_t swEccAlgorithm; uint32_t swEccBlockBytes; //!< [0x058-0x05b] uint8_t readyCheckOption; //!< [0x05c-0x05c] uint8_t statusCommandType; //!< [0x05d-0x05d] uint16_t readyCheckTimeoutInMs; //!< [0x05e-0x05f] uint16_t readyCheckIntervalInUs; //!< [0x060-0x061] uint8_t reserved0[30]; //!< [0x062-0x07f] uint8_t userOnfiAcTimingModeCode; //!< [0x080-0x080] uint8_t reserved1[31]; //!< [0x081-0x09f] uint32_t bytesInPageDataArea; //!< [0x0a0-0x0a3] uint32_t bytesInPageSpareArea; uint32_t pagesInBlock; uint32_t blocksInPlane; //!< [0x0ac-0x0af] uint32_t planesInDevice; //!< [0x0b0-0x0b3] uint32_t reserved2[19]; //!< [0x0b4-0x0ff] } semc_nand_config_t; typedef struct _semc_nand_fcb { semc_bcb_header_t bcbHeader; //!< [0x000-0x00b] uint32_t DBBTSerachAreaStartPage; //!< [0x00c-0x00f] uint16_t searchStride; //!< [0x010-0x011] uint16_t searchCount; //!< [0x012-0x013] uint32_t firmwareCopies; //!< [0x014-0x017] uint32_t reserved0[10]; //!< [0x018-0x03f] nand_firmware_info_t firmwareTable[SEMC_NAND_FW_MAX_NUM]; //!< [0x040-0x07f] uint32_t reserved1[32]; //!< [0x080-0x0ff] semc_nand_config_t nandConfig; //!< [0x100-0x1ff] uint32_t reserved2[128]; //!< [0x200-0x3ff] } semc_nand_fcb_t; typedef struct _semc_nand_dbbt { semc_bcb_header_t bcbHeader; //!< [0x000-0x00b] uint32_t reserved0; //!< [0x00c-0x00f] uint32_t badBlockNumber; //!< [0x010-0x013] uint32_t reserved1[3]; //!< [0x014-0x01f] uint32_t badBlockTable[SEMC_NAND_BAD_BLOCKS_MAX_NUM]; //!< [0x020-0x41f] } semc_nand_dbbt_t;
知道了FCB/DBBT結構,那麼怎麼生成FCB/DBBT數據而且下載進Raw NAND什麼地址處呢?固然咱們能夠手工建立FCB/DBBT並將其下載到Raw NAND中,但其實Flashloader工具會幫咱們自動作好大部分工做(生成FCB/DBBT,將FCB/DBBT下載到Raw NAND中),而咱們只須要提供簡化的12byte配置數據便可。若是你還有印象的話,痞子衡在啓動系列文章的第四篇 Flashloader初體驗(blhost) 的最後介紹過下載更新Application示例(該示例適用NAND芯片MX30LF4GE8AB-TI):
// 在SRAM裏臨時存儲Raw NAND配置數據 blhost -u -- fill-memory 0x2000 0x4 0xD0010101 // ONFI 1.0, non-EDO, Timing mode 0, 8bit IO, CSX0, HW ECC Check, inital HW ECC is enabled blhost -u -- fill-memory 0x2004 0x4 0x00010101 // image copy = 1, search stride = 1, search count = 1 blhost -u -- fill-memory 0x2008 0x4 0x00020001 // Firmware block index = 2, block count = 1 // 使用Raw NAND配置數據去配置Raw NAND接口 blhost -u -- configure-memory 0x100 0x2000
在上述示例裏痞子衡首先使用了fill-memory命令在0x2000地址處暫存了12byte配置數據,而後經過config-memory將這12byte數據裏的信息配置到Flashloader的Raw NAND接口中,實際上這4個命令成功執行後,FCB/DBBT就已經被下載進Raw NAND裏面了。那麼這12byte配置數據究竟是怎麼組織的?詳見下表:
從上表咱們能夠知道,其實這12byte數據提供的配置信息仍是比較多的,涵蓋NAND配置、FCB配置、Image配置,可是與FCB/DBBT本來的數據結構相比已經大幅精簡,咱們還能夠再進一步簡化,這12byte裏真正須要注意的只有四個地方(ECC status、ECC Type、IO Port Size、EDO mode),其他可用固定配置。因爲此處咱們示例NAND芯片爲MX30LF4GE8AB-TI,查看NAND芯片手冊可知其是x8 IO且沒有HW ECC,那麼IO Port Size需設2'b01(即x8),ECC type、ECC status分別可設1'b一、1'b0(HW ECC Check,initial HW ECC is enabled,其實這樣設在沒有HW ECC的NAND芯片上的意思是不使能ECC Check),EDO mode可設1'b0(即non-EDO模式)。
configure-memory命令執行成功以後,咱們能夠試着用read-memory從NAND芯片裏讀回FCB,DBBT確認一下,示例NAND芯片MX30LF4GE8AB-TI的page size爲2KB,block size爲128KB,那麼FCB應該在0x0處,DBBT應該在0x20000處,從0x0處讀回1KB數據發現其確實是有效的FCB,從FCB裏找到其中DBBTSerachAreaStartPage = 0x40,即DBBT放在index爲64的Page裏(即index爲1的Block起始Page裏),再從0x20000處讀回1056bytes數據發現其也確實是有效的DBBT。
解決了第一個問題即FCB/DBBT問題,還有另外一個問題即是image應該如何下載進Raw NAND?
其實image的下載很簡單,只須要將Bootable image從index爲2的block裏(與FCB裏的firmwareTable[0].startPage對應)開始下載便可,示例NAND芯片MX30LF4GE8AB-TI的block size爲128KB,則下載地址應爲0x40000,具體步驟以下:
// 擦除Raw NAND並將image下載進Raw NAND blhost -u -- flash-erase-region 0x40000 0x20000 0x100 // Erase 1 block starting from block 2 blhost -u -- write-memory 0x40000 ivt_image.bin 0x100 // Program ivt_image.bin to block 2
Bootable image下載成功以後,一樣咱們能夠試着用read-memory從NAND芯片裏讀回IVT,BootData,Application確認一下,Bootable image起始地址在0x40000,那麼IVT,BootData應該在0x40400,Application應該在0x42000,查看數據發現確實是有效的Bootable image。你可能會疑問,NAND的讀寫操做通常都是按page的,爲什麼咱們使用read-memory命令提供的地址參數能夠不按page對齊?其實Flashloader內部會有page緩存區,Flashloader底層對NAND的訪問是按page進行的,並緩存在內部page緩存區,接口上層來讀寫NAND數據實際是在page緩存區進行的,因此不受page對齊限制。
至此,Application的下載工做便結束了。
Application已經被成功下載進Raw NAND芯片以後,此時咱們即可以開始設置芯片從Raw NAND啓動:
在進入Boot Device選擇以前,你首先須要肯定BOOT_MODE[1:0]=2'b10,即芯片處於Internal Boot模式,而且確認BT_FUSE_SEL(eFUSE偏移0x460處的32bit配置數據的bit4)爲1'b0,這裏看不懂的朋友請溫習痞子衡前面的文章 Boot配置(BOOT Pin/eFUSE)。
設置好正確Boot模式後,再來選擇Boot Device,Boot Device由BOOT_CFG1[7:4]這四個pin的輸入狀態決定,下圖是RT105x/RT106x硬件板的參考設計,撥碼開關SW6應撥向SW_DIP-8的7,8,11,即設置BOOT_CFG[7:4]=4'b001x(4'b001x適用於i.MXRT105x/i.MXRT106x,對於i.MXRT102x此值應爲4'b01xx),此時便進入了從SEMC NAND啓動模式。
若是想確保i.MXRT芯片必定正在從Raw NAND啓動,可在芯片上電時使用Jlink調試器或者藉助Flashloader讀取芯片內部2個寄存器的值,這2個寄存器分別是SRC_SBMR1/2, 咱們設的關於啓動模式的BOOT_MODE pins/BOOT_CFG pin/eFUSE偏移0x450配置值在上電時會自動加載到SRC_SBMR1/2寄存器裏,BootROM主要是根據SRC_SBMR1/2寄存器的值來判斷啓動模式的。
PS: BOOT_MODE[1:0]也能夠設爲2'b00,即芯片處於Boot From Fuses模式,但此時稍微繁瑣一點,須要將BT_FUSE_SEL(eFUSE偏移0x460處的32bit配置數據的bit4)燒寫爲1'b1和BOOT_CFG1[7:4](eFUSE偏移0x450處的32bit配置數據的bit7:4)燒寫成4'b001x(適用於i.MXRT105x/i.MXRT106x)。
設置好芯片啓動模式是從Raw NAND啓動以後,咱們還須要最後關注一下與Raw NAND相關的具體特性配置:
你應該記得咱們在使用Flashloader下載Application的時候提供過12bytes的NAND配置數據,這12bytes的NAND配置數據是爲了讓Flashloader可以正確初始化Raw NAND接口去訪問NAND芯片(主要是寫FCB,DBBT,Bootable image),一樣BootROM上電也須要初始化Raw NAND接口去訪問NAND芯片(主要是讀FCB,DBBT,Bootable image),因此BootROM也須要相似這12bytes NAND配置數據,而BootROM的NAND配置便放在以下的eFUSE區域裏(i.MXRT105x/i.MXRT102x是同樣的,i.MXRT106x與i.MXRT105x比有細微調整),與Flashloader同樣這部分配置裏真正須要注意的也只有四個地方(ECC status、ECC Type、IO Port Size、EDO mode),其他可用初始配置(即0值)。
- i.MXRT105x A0版本與A1版本的BootROM有很大區別,本文內容僅適用於A1版本。
- 實測發現i.MXRT105x須要使能EDO模式方可訪問NAND(默認是AXI方式),而i.MXRT106x則不須要使能EDO模式。
- 配置數據(Flashloader的12bytes/BootROM的eFUSE)裏關於ECC的2處配置(ECC status、ECC Type)需根據實際鏈接的NAND芯片而定,不可隨意設置。
3.1 對於沒有硬件ECC的NAND芯片,可有兩種設置組合:1、ECC Type=HW,ECC status=Enabled(即不用ECC check);2、ECC Type=SW,ECC status=x(即便用SW ECC check)。
3.2 對於含有硬件ECC且默認是使能的NAND芯片,僅有一種設置組合:1、ECC Type=HW,ECC status=Enabled(即便用HW ECC check)。
3.3 對於含有硬件ECC且默認沒使能的NAND芯片,可有兩種設置組合:1、ECC Type=SW,ECC status=x(即便用SW ECC check);2、ECC Type=HW,ECC status=Disabled(即便用HW ECC check,BootROM會自動使用set-feature命令開啓硬件ECC,目前僅支持Micron的NAND芯片)
上述全部步驟所有完成以後,復位芯片你就應該能看到你放在Raw NAND裏的Application已經正常地啓動了。
至此,飛思卡爾i.MX RTyyyy系列MCU的Raw NAND啓動痞子衡便介紹完畢了,掌聲在哪裏~~~