大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是恩智浦i.MX RT1xxx系列MCU的Raw NAND啟動。
前面鋪墊了七篇啟動系列文章,終於該講具體Boot Device了,我們知道i.MXRT1xxx支持的外部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啟動,大家是不是很期待?(請配合說“是”),好,話不多說,開講。
一、支持的Raw 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及其之后的版本相對應。
Raw NAND廠商非常多,對應Raw NAND芯片型號也很多,如果你在選型時不確定到底該為i.MXRT選擇哪一款Raw NAND時,可選用下面五款芯片,痞子衡均實測過:
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芯片選型后,底下便進入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是其中一種。

雖然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芯片。

三、Raw NAND加載啟動過程
確保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為2、4的block均是壞塊,那么實際上Firmware被分散存儲在了index為3、5、6三個block中,為了將來能正確地從NAND中讀回Firmware,我們需要額外記錄至少兩個信息,一是指定的Firmware起始存儲地址0x40000,二是NAND中壞塊信息block index 2、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 1、2...7,直到找到有效的Firmware,這就是為什么在NAND中存儲多份Firmware的意義。
四、下載Application進Raw NAND
理解了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'b1、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的下載工作便結束了。
五、進入Raw NAND啟動模式
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)。
六、配置eFUSE啟動Raw NAND
設置好芯片啟動模式是從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芯片,可有兩種設置組合:一、ECC Type=HW,ECC status=Enabled(即不用ECC check);二、ECC Type=SW,ECC status=x(即使用SW ECC check)。
3.2 對於含有硬件ECC且默認是使能的NAND芯片,僅有一種設置組合:一、ECC Type=HW,ECC status=Enabled(即使用HW ECC check)。
3.3 對於含有硬件ECC且默認沒使能的NAND芯片,可有兩種設置組合:一、ECC Type=SW,ECC status=x(即使用SW ECC check);二、ECC Type=HW,ECC status=Disabled(即使用HW ECC check,BootROM會自動使用set-feature命令開啟硬件ECC,目前僅支持Micron的NAND芯片)
上述所有步驟全部完成之后,復位芯片你就應該能看到你放在Raw NAND里的Application已經正常地啟動了。
至此,恩智浦i.MX RT1xxx系列MCU的Raw NAND啟動痞子衡便介紹完畢了,掌聲在哪里~~~
歡迎訂閱
文章會同時發布到我的 博客園主頁、CSDN主頁、微信公眾號 平台上。
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。