linux开发环境搭建(4)-从SD卡启动uboot
1、superboot燒寫程序 
 前面已經將網卡配置好,也已經可以通過tftp下載程序到nand flash,也可以通過nfs掛載根文件系統,可以說基本的開發環境已經搭建好。
使用過友善開發板的人應該知道,友善對于在第一次空燒程序進nand flash里面的時候提供了一個叫superboot-6410.bin的sd卡啟動的uboot,要燒寫uboot到nand flash上的時候需要先將友善提供的superboot-6410.bin,可以在這里下載: 
 http://download.csdn.net/detail/atmega_chen/9782685
使用SD-Flasher.exe這個工具燒進sd卡里面,可以在這里下載: 
 http://download.csdn.net/detail/atmega_chen/9782681
如下圖,表示superboot-6410.bin已經燒寫到sd卡 
 
然后把sd卡插入到開發板上,并將撥碼開關撥到sd卡啟動,上電后會打印出下面的菜單 
 
從這些選項中可以看到,主要是可以下載uboot,kernel和filesystem,并且這個菜單沒有q選項,退出到uboot原始shell命令行,也就是說使用superboot.bin下載文件只能通過dnw使用usb下載,不能使用tftp下載。由于本人的電腦一直裝不上dnw的驅動,所以一直無法使用dnw下載程序,一般都是使用tftp下載uboot和kernel。但是友善提供的superboot.bin并沒有tftp下載的功能,而且也不提供源碼。所以就想移植一個已經有tftp下載功能的uboot,并且內從sd卡啟動這個uboot,這樣就可以通過從sd卡啟動,然后使用tftp功能下載文件。
2、如何從sd卡啟動 
 這里以移植好的tiny6410_uboot作為測試,這個是友善移植好的uboot,支持從sd卡啟動,可以在這里下載: 
 http://download.csdn.net/detail/atmega_chen/9772444
不過官方提供的代碼有些問題,主要是nand flash初始化部分,在從nand flash啟動的時候沒有問題,但是從sd卡啟動的時候要稍作修改,否則啟動不了。
了解uboot啟動流程的都知道,在uboot匯編階段的時候如果是從nand flash啟動,則BL1的代碼會從nand flash中把剩余的uboot代碼拷貝到內存中。同樣,如果要是從sd卡啟動,則會把剩余的uboot代碼從sd卡拷貝到內存,而三星的sd固件里面就提供了一個在啟動階段將數據從sd卡拷到內存的函數,這個函數是
#define CopyMovitoMem(a,b,c,d,e) (((int(*)(int, uint, ushort, uint *, int))(*((uint *)(TCM_BASE + 0x8))))(a,b,c,d,e))五個參數分別為a是使用的sd卡通道0或者1,b為BL2的長度,c為BL2的大小,d為要將BL2拷貝到內存的位置,d一般為0。 
 使用這個函數就可以把BL2從sd卡拷貝到內存,因為是從sd卡啟動,所以只要BL1放對了位置,開機的時候芯片內的固化代碼是會自動從sd卡上把BL1的代碼拷貝到setupping_stone上,然后運行的,我們需要做的是在BL1里面調用CopyMovitoMem函數把BL2拷貝到內存去。
3、編譯uboot從sd卡啟動 
 因為tiny6410_uboot是友善移植好的,支持uboot從sd卡啟動,所以我們嘗試編譯使用這個uboot,之后我們會自己移植,現在是搭建開發環境,先用移植好的。
進入到uboot目錄,打開頂層Makefile,搜索mini6410,可以看到有下面幾個配置選項,其中 
 mini6410_nand_config-ram128表示從nand flash啟動,RAM大小為128M; 
 mini6410_sd_config-ram128表示從sd卡啟動,RAM大小為128M; 
 tiny6410_config表示從nand flash啟動,RAM大小為256M; 
 mini6410_sd_config-ram256表示從sd卡啟動,RAM大小為256M; 
 我使用的開發板RAM大小為256M,因為要從sd卡啟動,所以選擇mini6410_sd_config-ram256。先配置,然后編譯 
 
編譯出來后可以通過dnw需要將uboot.bin放到sd里面去,下面介紹uboot.bin在sd卡中的位置。
4、uboot在sd卡中位置 
 由于之前把sd卡做成了superboot.bin的卡,現在要把卡恢復成正常卡,依然是使用SD-Flasher.exe,選擇ReFormat,然后再按scan,如果現實Available為no說明sd卡已經恢復正常,如下圖 
 
要讓uboot從sd卡啟動,則要正確燒寫uboot.bin在sd卡中的位置,這個具體位置可以參看三星的介紹SD卡啟動的文檔《CHxx_IROM_ApplicationNote_Rev1.00_080801.pdf》,可以在這里下載: 
 http://download.csdn.net/detail/atmega_chen/9782687
sd卡分為普通SD卡和大容量SDHC卡,大于2G的卡為SDHC卡,本人的SD卡為8G,所以屬于SDHC卡。對應不同容量的卡,uboot在sd卡中的位置不一樣,這個在文檔里面有說明,下圖是uboot.bin在SDHC卡中的位置。 
  
 從圖中可以知道,uboot啟動的第一階段代碼BL1,也就是uboot.bin的前8k代碼放在sd的最后的1025+1+16(block),BL2也就是完整的uboot.bin代碼放在1025+1+16+BL2_size(block)的地方,其中BL2_size為代碼定義的BL2的大小,比如友善的代碼就定義這個大小為0x60000。
所以BL1的位置為,以塊為單位: 
 BL1_pos = sdcard_size-1025-1-16; 
 BL2_pos = sdcard_size-1025-1-16-BL2_size;
下面是我寫的一個shell腳本,主要是自動讀取sd卡的容量,根據容量判斷是普通SD卡還是SDHC卡,然后確定BL1和BL2的位置,再把uboot.bin的前8k分離出來寫入到BL1的位置,整個uboot.bin寫入到BL2的位置。 
 注意需要使用root或者sudo權限運行,使用方法為插入sd卡,然后運行后會自動將uboot.bin文件燒寫入sd卡中。接受兩個參數,你的uboot.bin的路徑名,和sd卡的設備文件名: 
 ./sd_boot.sh ./u-boot.bin /dev/sdx
5、從sd卡啟動uboot 
 將編譯出來的uboot.bin通過sd_boot.sh腳本燒寫入sd后,將sd卡插入開發板,撥碼開關撥到從sd卡啟動,串口打印出了下面的log: 
 
從打印log看,sd卡中的代碼已經被正確拷貝到內存中,并且成功運行,說明前面的寫入的BL1和BL2的位置是正確,不過好像nand flash初始化的時候失敗了,提示沒有找到設備,而且emmc卡初始化也失敗。當nand flash啟動的時候,nand flash的初始化是正常的,從sd卡啟動時出現了錯誤。
通過分析代碼,在start_armboot的下面代碼是初始化nand flash的地方。
#if defined(CONFIG_NAND)puts ("NAND: ");nand_init(); /* go init the NAND */NAND_Init(); #endif繼續查看nand_init的代碼
void nand_init(void) {int i;unsigned int size = 0;for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);size += nand_info[i].size;if (nand_curr_device == -1)nand_curr_device = i;}printf("%lu MB ", size / (1024 * 1024));#if defined(CFG_NAND_FLASH_BBT)printf("(Flash Based BBT Enabled)"); #endifprintf("\n");#ifdef CFG_NAND_SELECT_DEVICE/** Select the chip in the board/cpu specific driver*/board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); #endif其中nand_init_chip是初始化nand_flash的代碼
static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,ulong base_addr) {mtd->priv = nand;nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;board_nand_init(nand);if (nand_scan(mtd, 1) == 0) {if (!mtd->name)mtd->name = (char *)default_nand_name;} elsemtd->name = NULL;}跟進到board_nand_init函數,這個函數比較長,貼取部分關鍵的代碼,在這里函數里面主要就是去通過NAND_CMD_READID命令讀取nand flash的ID,將讀取出來的ID和nand_flash_ids這個表里面已經有的ID進行比較,如果和這個表里面的某一項nand flash型號對的上說明讀取的ID是正確值,否則有可能讀取的ID是錯誤的。
從打印的錯誤log來看,這里讀取到的ID為0,說明讀取操作有問題。首先從硬件上排查,因為從nand flash啟動的時候,nand flash的初始化是正常的,所以可以肯定的是硬件上是沒問題的,而且整個代碼的邏輯也是沒問題的,這從nand flash啟動中可以知道,所以應該是讀取ID的操作出了問題。
s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);s3c_nand_device_ready(0);tmp = readb(nand->IO_ADDR_R); /* Maf. ID *///printf("Maf. ID :0x%x\n", tmp);tmp = readb(nand->IO_ADDR_R); /* Device ID *///printf("Device ID :0x%x\n", tmp); for (i = 0; nand_flash_ids[i].name != NULL; i++) {if (tmp == nand_flash_ids[i].id) {//printf("ID match, tmp=0x%x, nand_flash_ids=0x%x\n", tmp, nand_flash_ids[i].id);type = &nand_flash_ids[i];break;}}仔細想了一下,如果是從nand flash啟動,執行在這里初始化nand flash操作的時候其實nand flash已經在前面拷貝代碼的時候初始化過一遍,所以這里可以不需要重新初始化和復位nand flash。但是從sd卡啟動的話,前面沒有nand flash的相關操作,所以必須要重新初始化和復位才能正常讀寫。所以在讀取ID操作之前嘗試加了初始化操作和nand_reset復位函數,如下
void select_chip(void) {unsigned int cur =readl(NFCONT);cur &= ~(1<<1);writel(cur, NFCONT); }void delselect_chip(void) {unsigned int cur =readl(NFCONT);cur |= (1<<1);writel(cur, NFCONT); }void clear_RnB(void) {unsigned int cur =readl(NFSTAT);cur |= (1<<4);writel(cur, NFSTAT); }void send_cmd(unsigned char cmd) {writel(cmd, NFCMMD); }void send_addr(unsigned char addr) {writel(addr, NFADDR); }void wait_RnB(void) {while(!(readl(NFSTAT)&0x1)); }void nand_reset(void) {//選擇nandflashselect_chip();//清除RBclear_RnB();//發送0xffsend_cmd(0xff);//等待RB信號wait_RnB();//取消選擇nandflashdelselect_chip(); }/********************************/ //跳過部分代碼//add by CHB//初始化NFCONFcur = readl(NFCONF);cur &= ~((1<<12)|(2<<8)|(1<<4));cur |= (1<<12)|(2<<8)|(1<<4);writel(cur, NFCONF);//初始化NFCONTcur = readl(NFCONT);cur |= (0x1<<0) | (0x1<<1);writel(cur, NFCONT);//這里必須要復位一下,否則無法讀出IDnand_reset();s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);s3c_nand_device_ready(0);tmp = readb(nand->IO_ADDR_R); /* Maf. ID *///printf("Maf. ID :0x%x\n", tmp);tmp = readb(nand->IO_ADDR_R); /* Device ID *///printf("Device ID :0x%x\n", tmp);for (i = 0; nand_flash_ids[i].name != NULL; i++) {if (tmp == nand_flash_ids[i].id) {//printf("ID match, tmp=0x%x, nand_flash_ids=0x%x\n", tmp, nand_flash_ids[i].id);type = &nand_flash_ids[i];break;}}重新編譯燒寫uboot.bin到sd卡,上電后出現下面的打印log,可以看到nand flash已經被正確識別,說明我們的修改有作用,確實是nand flash沒有初始化和復位導致的。 
 
雖然nand flash的問題解決了,但是沒有看到uboot彈出shell命令行。可以看到emmc識別出來的大小為0,說明emmc的初始化也有問題,這里的emmc也就是我們的SD卡。 
 跟了下代碼,主要是發現是初始化SD卡的時候主機無法獲取到SD卡的OCR寄存器的值導致SD卡初始化失敗,然后進入了死循環,所以shell命令行出不來,這個問題目前沒有進一步深究下去。因為我們的SD卡主要是用來啟動uboot的,而啟動從sd卡讀取uboot主要使用的是sd卡的固件函數,所以這里只要能從sd卡將uboot讀取到內存去,那么sd的功能也就實現了,不需要用在uboot階段用sd卡來存儲文件。所以,我是直接將sd卡初始化這部分代碼直接注釋掉了,在start_armboot函數中:
注釋掉了以后,重新編譯燒寫uboot.bin到sd卡啟動,出現下面的打印log,可以看到uboot已經正常啟動了,并且菜單也打印出來了,說明uboot已經正確從sd卡啟動了。 
 
選q退出菜單,回到原始命令行,輸入tftp 0xc0008000 uImage 
 可以看到我們也可以從sd卡啟動通過tftp下載程序到內存了。 
 
下一節會介紹如何使用tftp和nand命令結合下載uboot到nand flash,需要修改一些nand flash的命令。
總結
以上是生活随笔為你收集整理的linux开发环境搭建(4)-从SD卡启动uboot的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 计算机有关的文献检索题目,文献检索第二次
- 下一篇: 电网交流采样
