u-boot for tiny210 ver3.0 (by liukun321咕唧咕唧)

在此首先要特別感謝網友李明老師和Alex Ling對我無私的幫助和支持。

這次更新,主要實現了Nand啓動,並修改了前幾個版本的幾個小bug。ver3.0已經基本完成了u-boot的主線功能。後面我還會繼續更新其它輔助功能。

之前上傳的幾個版本,對nandflash燒寫時ECC校驗是基於軟件ECC,由於S5PV210的IROM中固化的啓動代碼(暫且稱其爲BL0)在讀nandflash時

用的是8bit 硬件ECC。因此在燒寫u-boot for tiny210到nandflash時,需要用開啓了8bit 硬件ECC的 SD卡啓動的u-boot 進行燒寫(詳見後面分析)。

 您可以從下面的鏈接獲得源碼,也可以下載歷史版本,並參考後面的步驟修改獲得ver3.0.

ver3.0源碼下載:u-boot for tiny 210 ver3.0

 您也可以打開下面的鏈接閱讀ver3.0源碼

Git source tree(這是我在Gitorious建的第一個工程,對它的管理還是顯的比較混亂。等我整理好另一個clone的工程,我會把鏈接貼上來)。

 

下面的鏈接提供了歷史版本的源碼

ver2.2源碼下載:  u-boot for tiny210 ver2.2

ver2.1源碼下載:u-boot for tiny210 ver2.2

ver2.1源碼下載:u-boot for tiny210 ver2.1

ver2.0源碼下載:u-boot for tiny210 ver2.0

各版本修改分析鏈接:ver2.0  ver2.1  ver2.2 ver2.2.1 ver2.2.2
 

ver3.0的基本功能:

1. SD boot,基於linaro u-boot的SPL功能實現

2. 從SD卡的FAT分區上加載文件到SDRAM

3. 將環境變量保存至SD卡

4. 添加DM9000網卡驅動,開啓網絡功能(例如:tftp,nfs等)

5. 添加TAB鍵命令自動補全功能

6.修復bug:

修復bug 1:SD卡保存環境變量出現Writing to MMC(0)... mmc_send_cmd: error during transfer: 0x00208001 mmc write failed。

修復bug 2:每次啓動只能保存一次環境變量。

7.添加NandFlash驅動,開啓所有Nand cmd。

8.添加Yaffs文件系統燒寫支持。

+9.修改在SD卡啓動時對nandflash的燒寫爲8bit 硬件ECC校驗。(nand啓動仍爲軟件ECC)

+10.添加Nandflash啓動。
 
在介紹修改步驟之前,先對S5PV210的啓動流程,及BL0階段需要注意的問題作簡要分析:
(1)啓動流程:
啓動的詳細過程見datasheet,如下:
Program code starts from internal ROM(iROM) and moves to internal SRAM(iRAM). Finally, program executes on DRAM.
The booting sequence in internal ROM is as follows:
1. Disable the watchdog timer.
2. Initialize the instruction cache controller.
3. Initialize the stack and heap region.
4. Check secure key.
5. Set Clock divider, lock time, PLL (MPS value), and source clock.
6. Check OM pin and load the first boot loader (The size of boot loader depends on S/W) from specific device (block number 0) to iRAM.
7. If secure booting is successful, execute integrity check
8. If integrity check passes, then jump to the first boot loader in iRAM (0xD002_0010)
The booting sequence in internal SRAM is as follows:
1. Load the second boot loader from boot device to iRAM.
2. If secure booting is successful, execute integrity check.
3. If integrity check passes, then jump to the second boot loader in iRAM (The jumping address depends on user's software)
4. If integrity check fails, then stop the first boot loader.
5. The second boot loader Initializes the DRAM controller.
6. Load the OS image from specific device (block number 1) to DRAM.
7. Jump to OS code in DRAM (0x2000_0000 or 0x4000_0000)
(2)BL0階段需要注意的幾個問題:
1.在BL0階段 nand或SD啓動,前16K內容搬運到IRAM。
S5PV210有64k的irom,裏面固化的代碼用於將Nandflash或sd卡中的前16K代碼cp到IRAM中(這個過程暫且稱之爲BL0,例如:在2440中的nand啓動時BL0複製的是nandflash的前4K內容),只有在BL0成功完成以後,u-boot的BL1和BL2階段才能夠完成。IROM和Iram在地址空間的分佈如下圖:
 
 
2.關於BL0階段的ECC校驗。
 由於我之前用過2440,就拿2440與s5pv210對比分析。2440在BL0階段讀nandflash時並沒有開啓ECC校驗,而s5pv210在BL0階段開啓了8bit 硬件ECC校驗。所以我們在nandflash中燒寫uboot時要開啓8bit 硬件ECC校驗(因此我將ver3.0的SDboot版對nand的讀寫修改爲8bit 硬件ECC校驗,用於向nandflash燒寫uboot)。如
果用非8bit 硬件ECC校驗的方式向nandflash燒寫uboot,BL0階段就會stop(ECC校驗錯誤:這是導致BL0 stop的第一個原因)。
ECC校驗的算法有很多種,至於用哪一種,我是通過分析用Superboot210燒寫過的nandflash的oob區猜得的:
 
通過uboot的nand dump命令 ,如上圖我用nand dump 0 讀取了第零頁及其oob的內容,該校驗算法使用了52個字節的oob區,而tiny210用的nand的每page大小爲2048byte。
從datasheet可也以得到:
這似乎可以聯繫到BCH算法(詳細內容見附錄)。因爲以8bit/512Byte BCH方式,控制器寫數據到NAND Flash時,每512Byte數據經過BCH模塊就會生成13Byte的校驗數據。這樣修改 8bit  硬件ECC校驗(SD啓動的uboot開啓了此校驗方式,因爲我們要用它在nand中燒寫uboot)的基本方向就確定了。更多關於ECC的信息請參考手冊。
 
3.關於spl/tiny210-spl.bin文件前16字節內容的分析。
u-boot for tiny210 是基於spl的。
在使用前幾個版本的u-boot for tiny210時,我們會把spl/tiny210-spl.bin燒到SD卡第一扇區。分析spl/Makefile 及 u-boot-spl.map我們可以知道:libtiny210.o(start.S)  lowlevel_init.o  mem_setup.o  nand_cp.o  nand.o  這幾個文件會被最先鏈接生成.bin,該.bin文件經board/samsung/tiny210/tools/mktiny210spl.exe處理後最終得到tiny210-spl.bin。mktiny210spl.exe是由board/samsung/tiny210/tools/mkv210_image.c文件得到。
 
board/samsung/tiny210/tools/mktiny210spl.exe處理過程:根據mkv210_image.c文件的內容不難看出它的功能是將未被處理的.bin文件內容讀到BUFSIZE大小的緩衝區,然後統計.bin文件中1的個數(作爲用於BL0過程的校驗和)。校驗和存放在tiny210-spl.bin 文件最前面16字節的高8個字節中,而低8個字節則是存放#define SPL_HEADER              "S5PC110 HEADER  "字串的前8個字符也就是"S5PC110 "   。高8個字節的校驗和將直接決定BL0階段是否能夠成功完成(校驗和錯誤:這是導致BL0 stop的第二個原因)。當#define IMG_SIZE                (X*1024)大於未被處理的.bin文件時,生成的新文件尾會被0填充直至生成的tiny210-spl.bin的大小等於IMG_SIZE。
 
在BL0階段,Irom內固化的代碼讀取nandflash或SD卡前16K的內容,並比對前16字節中的校驗和是否正確,正確則繼續,錯誤則停止。
 
a = Buf + SPL_HEADER_SIZE;
 for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
  checksum += (0x000000FF) & *a++;
上面這段代碼位於board/samsung/tiny210/tools/mkv210_image.c中即是統計校驗和。
(3)下面介紹一下修改步驟(以下步驟是基於u-boot for tiny210 ver2.2.X的),並作簡要的分析。
 
1.首先在include/configs/tiny210.h 中修改下述宏:
刪除172-183行:

-/* FLASH and environment organization */
-#define CONFIG_SYS_NO_FLASH  1
-#undef CONFIG_CMD_IMLS
-#define CONFIG_IDENT_STRING " for FriendlyLEG-TINY210"
-
-#define CONFIG_ENV_IS_IN_MMC  1
-#define CONFIG_SYS_MMC_ENV_DEV  0
-#define CONFIG_ENV_SIZE  0x4000 /* 16KB */
-#define RESERVE_BLOCK_SIZE              (512)
-#define BL1_SIZE                        (8 << 10) /*8 K reserved for BL1*/
-#define CONFIG_ENV_OFFSET               (RESERVE_BLOCK_SIZE + BL1_SIZE + ((16 + 512) * 1024))
-#define CONFIG_DOS_PARTITION  1

然後在

460:

+/* FLASH and environment organization */
+#define CONFIG_SYS_NO_FLASH             1
+#undef CONFIG_CMD_IMLS
+#define CONFIG_IDENT_STRING     " for FriendlyLEG-TINY210"
+#if 1
+#define CONFIG_TINY210_NAND_BOOT               1
+#endif
+#if 0
+#define CONFIG_TINY210_MMC_BOOT               1
+#endif
+/*MMC BOOT   */
+#if defined(CONFIG_TINY210_MMC_BOOT)
+#define CFG_NAND_HWECC
+#define CONFIG_NAND_BL1_8BIT_ECC
+#define CONFIG_ENV_IS_IN_MMC            1
+#define CONFIG_SYS_MMC_ENV_DEV          0
+#define CONFIG_ENV_SIZE         0x4000  /* 16KB */
+#define RESERVE_BLOCK_SIZE              (512)
+#define BL1_SIZE                        (8 << 10) /*8 K reserved for BL1*/
+#define CONFIG_ENV_OFFSET               (RESERVE_BLOCK_SIZE + BL1_SIZE + ((16 + 512) * 1024))
+#endif
+
+#define CONFIG_DOS_PARTITION            1
+
+/*NAND_BOOT  by lk  */
+#if defined(CONFIG_TINY210_NAND_BOOT)

+#define CONFIG_S5PC11X
+#define CONFIG_ENV_IS_IN_NAND            1
+#define CONFIG_ENV_SIZE         0x4000  /* 16KB */
+#define CONFIG_ENV_OFFSET               0x40000

+#endif

 

#define CONFIG_TINY210_NAND_BOOT               1
即可實現nand啓動

#define CONFIG_TINY210_MMC_BOOT               1

即可實現sd啓動

2.修改board/samsung/tiny210/Makefile 即可通過定義CONFIG_TINY210_MMC_BOOT/CONFIG_TINY210_NAND_BOOT,分別實現SD啓動和NAND啓動。
ifdef CONFIG_SPL_BUILD
+
+ifdef CONFIG_TINY210_MMC_BOOT
 COBJS  += mmc_boot.o
 endif

+ifdef CONFIG_TINY210_NAND_BOOT
 COBJS  += nand_cp.o
+endif

+endif
屏蔽掉38行的COBJS  += nand_cp.o

 

3.修改實現8bit 硬件ECC校驗。board/samsung/tiny210/nand.c中

44:static struct nand_ecclayout s3c_nand_oob_16 = {
  .eccbytes = 4,
  .eccpos = {1, 2, 3, 4},
  .oobfree = {
   {.offset = 6,
    . length = 10}}
 };
-
52:+static struct nand_ecclayout s3c_nand_oob_64_8bit = {
+ .eccbytes = 52,
+ .eccpos = {
+   12,13,14,15,
+   16,17,18,19,20,21,22,23, 
+     24, 25, 26, 27, 28, 29, 30, 31,
+     32, 33, 34, 35, 36, 37, 38, 39,
+     40, 41, 42, 43, 44, 45, 46, 47,
+      48, 49, 50, 51, 52, 53, 54, 55,
+        56, 57, 58, 59, 60, 61, 62, 63},
+ .oobfree = {
+  {.offset = 2,
+   .length = 10}}
+};
 /* Nand flash oob definition for SLC 2k page size by jsgood */
 static struct nand_ecclayout s3c_nand_oob_64 = {

屏蔽掉下面兩個函數中的while

上面的信息可見【25】【24】位是對MLC nandflash的狀態位,而tiny210上用的是SLC的nandflash。所以進行下面的屏蔽。

 static void s3c_nand_wait_enc(void)
 {
+// while (!(readl(NFECCSTAT) & NFSTAT_ECCENCDONE)) {}
 }
 
 /*
@@ -187,7 +200,7 @@ static void s3c_nand_wait_enc(void)
  */
 static void s3c_nand_wait_dec(void)
 {
+// while (!(readl(NFECCSTAT) & NFSTAT_ECCDECDONE)) {}
 }

 

@@ -1042,9 +1067,24 @@

 int board_nand_init(struct nand_chip *nand)
    nand_type = S3C_NAND_TYPE_SLC;
    nand->ecc.size = 512;
    nand->ecc.bytes = 4;
-
1060:+ 
+ if ((1024 << (tmp & 3)) == 4096) {
+     /* Page size is 4Kbytes */
+ nand->ecc.read_page = s3c_nand_read_page_8bit;
+ nand->ecc.write_page = s3c_nand_write_page_8bit;
+ nand->ecc.read_oob = s3c_nand_read_oob_8bit;
+ nand->ecc.write_oob = s3c_nand_write_oob_8bit;
+ nand->ecc.layout = &s3c_nand_oob_128;
+ nand->ecc.hwctl = s3c_nand_enable_hwecc_8bit;
+ nand->ecc.calculate = s3c_nand_calculate_ecc_8bit;
+ nand->ecc.correct = s3c_nand_correct_data_8bit;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 13;
+ nand->options |= NAND_NO_SUBPAGE_WRITE;}
+  else
    if ((1024 << (tmp & 0x3)) > 512) {
-    nand->ecc.read_page = s3c_nand_read_page_1bit;
1073:+#if defined(CONFIG_NAND_4BIT_ECC)

+   nand->ecc.read_page = s3c_nand_read_page_1bit;
     nand->ecc.write_page = s3c_nand_write_page_1bit;
     nand->ecc.read_oob = s3c_nand_read_oob_1bit;
     nand->ecc.write_oob = s3c_nand_write_oob_1bit;
@@ -1053,6 +1093,18 @@ int board_nand_init(struct nand_chip *nand)
                                 nand->ecc.calculate = s3c_nand_calculate_ecc;
                                 nand->ecc.correct = s3c_nand_correct_data;
                                 nand->options |= NAND_NO_SUBPAGE_WRITE;

1083:+#endif
+nand->ecc.read_page = s3c_nand_read_page_8bit;
+ nand->ecc.write_page = s3c_nand_write_page_8bit;
+ nand->ecc.read_oob = s3c_nand_read_oob_8bit;
+ nand->ecc.write_oob = s3c_nand_write_oob_8bit;
+ nand->ecc.layout = &s3c_nand_oob_64_8bit;
+ nand->ecc.hwctl = s3c_nand_enable_hwecc_8bit;
+ nand->ecc.calculate = s3c_nand_calculate_ecc_8bit;
+ nand->ecc.correct = s3c_nand_correct_data_8bit;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 13;
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
    } else {
     nand->ecc.layout = &s3c_nand_oob_16;
    }

4,修改nand_cp.c文件。

+//#include <regs.h>
+#include <s5pc110.h>

+#define COPY_BL2_SIZE  0x80000

@@ -98,7 +98,7 @@ static int nandll_read_blocks (ulong dst_addr, ulong size, int large_block)
   page_shift = 11;
 
         /* Read pages */
98:        for (i =(0x6000>>page_shift); i < (size>>page_shift); i++, buf+=(1<<page_shift)) {
                 nandll_read_page(buf, i, large_block);
         }

0x6000是24k後的第一個地址,也就是從這個nandflash的這個偏移地址開始cp內容到DRAM。

 

@@ -128,8 +128,29 @@ int copy_uboot_to_ram (void)

128:+ return nandll_read_blocks(CONFIG_SYS_TEXT_BASE, COPY_BL2_SIZE, large_block);
+}
+void board_init_f(unsigned long bootflag)
+{
+        __attribute__((noreturn)) void (*uboot)(void);
+        copy_uboot_to_ram();
+
+        /* Jump to U-Boot image */
+        uboot = (void *)CONFIG_SYS_TEXT_BASE;
+        //while(1);
+ (*uboot)();
+        /* Never returns Here */
 }
-
+
+/* Place Holders */
+void board_init_r(gd_t *id, ulong dest_addr)
+{
+        /* Function attribute is no-return */
+        /* This Function never executes */
+        while (1)
+                ;
+}
+
+void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3) {}

 

5.修改board/samsung/tiny210/tools/mkv210_image.c

8:#define BUFSIZE                 (24*1024)
      #define IMG_SIZE                (24*1024)

這個參數的修改是爲了便於實現nandboot和SDboot的切換,因爲
在board/samsung/tiny210/mmc_boot.c第四十九行可以找到

#define MOVI_BL2_POS            ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_BL1_BLKCNT + MOVI_ENV_BLKCNT)
當sdboot的BL1階段要從MOVI_BL2_POS(24k + 1k(sd卡的第0扇區))這個偏移位置開始向DRAM cp代碼。雖然啓動時只會從nand或SD卡一次性讀取16K內容,但是爲了統一兩種啓動方式tiny210-spl.bin的大小,生成24k大小的tiny210-spl.bin(這並不會影響校驗和,因爲文件尾是由0填充)。

另外我在源碼的根目錄下寫了一個簡單的腳本make-tiny210-boot.sh,用於合併tiny210-spl.bin和u-boot.bin文件->生成tiny210-uboot.bin。這樣我們以後燒寫uboot時就不用像老版本那樣分別燒寫兩個文件了。

至此ver3,0修改完成.

編譯u-boot
$make ARCH=arm CROSS_COMPILE=/opt/FriendlyARM/toolschain/4.5.1/bin/arm-none-linux-gnueabi- tiny210_config
$make ARCH=arm CROSS_COMPILE=/opt/FriendlyARM/toolschain/4.5.1/bin/arm-none-linux-gnueabi- all spl
$./make-tiny210-boot.sh
由於我的系統下裝有兩套交叉工具鏈,所以沒有把 /opt/FriendlyARM/toolschain/4.5.1/bin/ 添加到環境變量,在使用工具鏈時要指明路徑。

1.sd啓動
當我們在include/configs/tiny210.h定義了#define CONFIG_TINY210_MMC_BOOT               1 爲SD啓動
 
將u-boot鏡像寫入SD卡
將SD卡通過讀卡器接上電腦(或直接插入筆記本卡槽),通過"cat /proc/partitions"找出SD卡對應的設備,我的設備節點是/dev/sdb.

執行下面的命令
$sudo dd iflag=dsync oflag=dsync if=tiny210-uboot.bin of=/dev/sdb seek=1
2.nandflash啓動
當我們在include/configs/tiny210.h定義了#define CONFIG_TINY210_NAND_BOOT               1 爲NANDFLASH啓動
通過SD卡啓動的u-boot for tiny210 將u-boot鏡像寫入nandflash

 

[FriendlyLEG-TINY210]# tftp 21000000 tiny210-uboot.bin
[FriendlyLEG-TINY210]# nand erase.chip
[FriendlyLEG-TINY210]# nand write 21000000 0 3b4c4
 
 
測試保存環境變量:
 
 
附錄:BCH算法

對於BCH算法目前通常以512Byte或者1024Byte爲單位處理,因爲BCH按位處理數據,所以是4096bit或者8192bit,這裏的4096/8192bit是原始數據,BCH需要生成一定的校驗數據。下面簡要介紹下原理

設最大糾錯能力爲t

如果選用4096bit的原始數據長度,則模式爲BCH(8191,8191-13×t,t,13)

如果選用8192bit的原始數據長度,則模式爲BCH(16383,16383-14×t,t,14)

校驗數據長度就是13×t,或者14×tbit

所以平均1024+32Byte的MLC 大多建議使用8bit/512Byte ECC

平均1024+45Byte的MLC大多建議使用24Bit/1024Byte ECC, 此時需要14×24bit=42Byte的檢驗數據空間

以8bit/512Byte BCH方式的ECC爲例,控制器寫數據到NAND Flash時,每512Byte數據經過BCH模塊就會生成13Byte的校驗數據(當然剩下的16-13=3Byte也可以作爲某種用途的數據,可以任意使用0-3Byte而不會改變ECC的使用),這些數據一起寫入到NAND Flash中。控制器從NAND Flash中讀取數據的時候需要將原始數據和校驗數據一起讀出經過BCH模塊,BCH模塊計算伴隨矩陣首先可以判斷出是否出現了錯誤,如果出現了錯誤需要計算錯誤位置多項式,然後解多項式,得到錯誤位置(目前主要使用Chien-search方法),因爲是位錯誤,找到錯誤的位置以後取反以後就是正確的數據。只要是錯誤個數小於等於8,BCH都能夠找到錯誤的位置,但是如果錯誤個數超過了8,對於BCH來說已經沒有辦法糾正錯誤了,只能報告出現了不可以糾正的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章