嵌入式培訓

                          嵌入式Linux就業班馬上開課了 詳情點擊這兒

                           
                          上海:021-51875830
                          北京:010-51292078
                          南京:4008699035
                          石家莊:4008699035

                          廣州:4008699035
                          西安:029-86699670
                          深圳:4008699035
                          武漢:027-50767718
                          成都:4008699035
                          曙海研發與生產請參見網址:www.shanghai66.cn
                          全英文授課課程(Training in English)
                            首 頁  手機閱讀  課程介紹   培訓報名  企業培訓   付款方式   講師介紹   學員評價  關于我們   聯系我們   承接項目 實驗板   網校
                          嵌入式協處理器--DSP
                          嵌入式協處理器--FPGA
                          FPGA項目實戰系列課程----
                          嵌入式OS--4G手機操作系統
                          嵌入式OS-Linux
                          嵌入式CPU--ARM

                          嵌入式OS--WinCE
                          單片機培訓
                          嵌入式硬件設計
                          嵌入式OS--VxWorks
                          友情連接
                          學員VIP專區
                          用戶名:

                          密碼: 
                           
                          WEB在線客服
                          南京WEB在線客服
                          武漢WEB在線客服
                          西安WEB在線客服
                          廣州WEB在線客服
                          點擊這里給我發消息  
                          QQ客服一
                          點擊這里給我發消息  
                          QQ客服二
                          點擊這里給我發消息
                          QQ客服三
                            雙休日、節假日及晚上可致電值班電話:021-51875830 值班手機:15921673576/13918613812

                          值班QQ:
                          點擊這里給我發消息

                          值班網頁在線客服,點擊交談:
                           
                          網頁在線客服

                           
                          合作伙伴與授權機構
                          現代化的多媒體教室
                          公益培訓


                           

                             

                          本文從以下幾個方面粗淺地分析u-boot并移植到FS2410板上:
                          1、u-boot工程的總體結構
                          2、u-boot的流程、主要的數據結構、內存分配。
                          3、u-boot的重要細節,主要分析流程中各函數的功能。
                          4、基于FS2410板子的u-boot移植。實現了NOR Flash和NAND Flash啟動,網絡功能!
                          這些認識源于自己移植u-boot過程中查找的資料和對源碼的簡單閱讀。下面主要以smdk2410為分析對象。

                          一、u-boot工程的總體結構:
                          1、源代碼組織
                          對于ARM而言,主要的目錄如下:
                          board 平臺依賴  存放電路板相關的目錄文件,每一套板子對 應一個目錄。如smdk2410(arm920t)
                           
                          cpu 平臺依賴  存放CPU相關的目錄文件,每一款CPU對應一個目錄,例如:arm920t、 xscale、i386等目錄
                          lib_arm 平臺依賴  存放對ARM體系結構通用的文件,主要用于實現ARM平臺通用的函數,如軟件浮點。

                          common 通用 通用的多功能函數實現,如環境,命令,控制臺相關的函數實現。
                          include 通用 頭文件和開發板配置文件,所有開發板的配置文件都在configs目錄下
                          lib_generic 通用 通用庫函數的實現
                          net 通用 存放網絡協議的程序
                          drivers 通用 通用的設備驅動程序,主要有以太網接口的驅動,nand驅動。
                          .......
                          2.makefile簡要分析
                          所有這些目錄的編譯連接都是由頂層目錄的makefile來確定的。
                          在執行make之前,先要執行make $(board)_config 對工程進行配置,以確定特定于目標板的各個子目錄和頭文件。
                          $(board)_config:是makefile 中的一個偽目標,它傳入指定的CPU,ARCH,BOARD,SOC參數去執行mkconfig腳本。
                          這個腳本的主要功能在于連接目標板平臺相關的頭文件夾,生成config.h文件包含板子的配置頭文件。
                          使得makefile能根據目標板的這些參數去編譯正確的平臺相關的子目錄。
                          以smdk2410板為例,執行 make smdk2410_config,
                          主要完成三個功能:
                          @在include文件夾下建立相應的文件(夾)軟連接,

                          #如果是ARM體系將執行以下操作:
                          #ln -s asm-arm asm
                          #ln -s arch-s3c24x0 asm-arm/arch
                          #ln -s proc-armv asm-arm/proc

                          @生成Makefile包含文件include/config.mk,內容很簡單,定義了四個變量:

                          ARCH = arm
                          CPU = arm920t
                          BOARD = smdk2410
                          SOC = s3c24x0

                          @生成include/config.h頭文件,只有一行:

                          /* Automatically generated - do not edit */
                          #include "config/smdk2410.h"

                          頂層makefile先調用各子目錄的makefile,生成目標文件或者目標文件庫。
                          然后再連接所有目標文件(庫)生成終的u-boot.bin。
                          連接的主要目標(庫)如下:
                          OBJS = cpu/$(CPU)/start.o
                          LIBS = lib_generic/libgeneric.a
                          LIBS += board/$(BOARDDIR)/lib$(BOARD).a
                          LIBS += cpu/$(CPU)/lib$(CPU).a
                          ifdef SOC
                          LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
                          endif
                          LIBS += lib_$(ARCH)/lib$(ARCH).a
                          LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
                          fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
                          LIBS += net/libnet.a
                          LIBS += disk/libdisk.a
                          LIBS += rtc/librtc.a
                          LIBS += dtt/libdtt.a
                          LIBS += drivers/libdrivers.a
                          LIBS += drivers/nand/libnand.a
                          LIBS += drivers/nand_legacy/libnand_legacy.a
                          LIBS += drivers/sk98lin/libsk98lin.a
                          LIBS += post/libpost.a post/cpu/libcpu.a
                          LIBS += common/libcommon.a
                          LIBS += $(BOARDLIBS)
                          顯然跟平臺相關的主要是:
                          cpu/$(CPU)/start.o
                          board/$(BOARDDIR)/lib$(BOARD).a 
                          cpu/$(CPU)/lib$(CPU).a
                          cpu/$(CPU)/$(SOC)/lib$(SOC).a 
                          lib_$(ARCH)/lib$(ARCH).a
                          這里面的四個變量定義在include/config.mk(見上述)。
                          其余的均與平臺無關。
                          所以考慮移植的時候也主要考慮這幾個目標文件(庫)對應的目錄。

                          關于u-boot 的makefile更詳細的分析可以參照http://blog.mcuol.com/User/lvembededsys/Article/4355_1.htm。

                          3、u-boot的通用目錄是怎么做到與平臺無關的?
                          include/config/smdk2410.h
                          這個頭文件中主要定義了兩類變量。
                           一類是選項,前綴是CONFIG_,用來選擇處理器、設備接口、命令、屬性等,主要用來 決定是否編譯某些文件或者函數。

                          另一類是參數,前綴是CFG_,用來定義總線頻率、串口波特率、Flash地址等參數。這些常數參量主要用來支持通用目錄中的代碼,定義板子資源參數。

                          這兩類宏定義對u-boot的移植性非常關鍵,比如drive/CS8900.c,對cs8900而言,很多操作都是通用的,但不是所有的板子上面都有這個芯片,即使有它在內存中映射的基地址也是平臺相關的。所以對于smdk2410板,在smdk2410.h中定義了
                          #define CONFIG_DRIVER_CS8900 1 /* we have a CS8900 on-board */
                          #define CS8900_BASE 0x19000300 /*IO mode base address*/
                          CONFIG_DRIVER_CS8900的定義使得cs8900.c可以被編譯(當然還得定義CFG_CMD_NET才行),因為cs8900.c中在函數定義的前面就有編譯條件判斷:#ifdef CONFIG_DRIVER_CS8900 如果這個選項沒有定義,整個cs8900.c就不會被編譯了。
                          而常數參量CS8900_BASE則用在cs8900.h頭文件中定義各個功能寄存器的地址。u-boot的CS8900工作在IO模式下,只要給定IO寄存器在內存中映射的基地址,其余代碼就與平臺無關了。

                          u-boot的命令也是通過目標板的配置頭文件來配置的,比如要添加ping命令,就必須添加CFG_CMD_NET和CFG_CMD_PING才行。不然common/cmd_net.c就不會被編譯了。
                          從這里我可以這么認為,u-boot工程可配置性和移植性可以分為兩層:
                          一是由makefile來實現,配置工程要包含的文件和文件夾上,用什么編譯器。
                          二是由目標板的配置頭文件來實現源碼級的可配置性,通用性。主要使用的是#ifdef #else #endif 之類來實現的。
                          4、smkd2410其余重要的文件:
                          include/s3c24x0.h   定義了s3x24x0芯片的各個特殊功能寄存器(SFR)的地址。
                          cpu/arm920t/start.s 在flash中執行的引導代碼,也就是bootloader中的stage1,負責初始化硬件環境,把u-boot從flash加載到RAM中去,然后跳到lib_arm/board.c中的start_armboot中去執行。
                          lib_arm/board.c   u-boot的初始化流程,尤其是u-boot用到的全局數據結構gd,bd的初始化,以及設備和控制臺的初始化。
                          board/smdk2410/flash.c 在board目錄下代碼的都是嚴重依賴目標板,對于不同的CPU,SOC,ARCH,u-boot都有相對通用的代碼,但是板子構成卻是多樣的,主要是內存地址,flash型號,外圍芯片如網絡。對fs2410來說,主要考慮從smdk2410板來移植,差別主要在nor flash上面。

                          二、u-boot的流程、主要的數據結構、內存分配
                          1、u-boot的啟動流程:
                            從文件層面上看主要流程是在兩個文件中:cpu/arm920t/start.s,lib_arm/board.c, 
                            1)start.s 
                          在flash中執行的引導代碼,也就是bootloader中的stage1,負責初始化硬件環境,把u-boot從flash加載到RAM中去,然后跳到lib_arm/board.c中的start_armboot中去執行。
                          1.1.6版本的start.s流程:
                          硬件環境初始化:
                            進入svc模式;關閉watch dog;屏蔽所有IRQ掩碼;設置時鐘頻率FCLK、HCLK、PCLK;清I/D cache;禁止MMU和CACHE;配置memory control;
                          重定位:
                            如果當前代碼不在連接指定的地址上(對smdk2410是0x3f000000)則需要把u-boot從當前位置拷貝到RAM指定位置中;
                          建立堆棧,堆棧是進入C函數前必須初始化的。
                          清.bss區。
                          跳到start_armboot函數中執行。(lib_arm/board.c)
                          2)lib_arm/board.c:
                           start_armboot是U-Boot執行的第一個C語言函數,完成系統初始化工作,進入主循環,處理用戶輸入的命令。這里只簡要列出了主要執行的函數流程:
                          void start_armboot (void)
                          {
                          //全局數據變量指針gd占用r8。
                          DECLARE_GLOBAL_DATA_PTR;

                          /* 給全局數據變量gd安排空間*/
                          gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
                          memset ((void*)gd, 0, sizeof (gd_t));

                          /* 給板子數據變量gd->bd安排空間*/
                          gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
                          memset (gd->bd, 0, sizeof (bd_t));
                          monitor_flash_len = _bss_start - _armboot_start;//取u-boot的長度。

                          /* 順序執行init_sequence數組中的初始化函數 */
                          for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
                          if ((*init_fnc_ptr)() != 0) {
                          hang ();
                          }
                          }

                          /*配置可用的Flash */
                          size = flash_init ();
                           ……
                          /* 初始化堆空間 */
                          mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
                          /* 重新定位環境變量, */
                          env_relocate ();
                          /* 從環境變量中獲取IP地址 */
                          gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
                          /* 以太網接口MAC 地址 */
                          ……
                          devices_init (); /* 設備初始化 */
                          jumptable_init (); //跳轉表初始化
                          console_init_r (); /* 完整地初始化控制臺設備 */
                          enable_interrupts (); /* 使能中斷處理 */
                          /* 通過環境變量初始化 */
                          if ((s = getenv ("loadaddr")) != NULL) {
                          load_addr = simple_strtoul (s, NULL, 16);
                          }
                          /* main_loop()循環不斷執行 */
                          for (;;) {
                          main_loop (); /* 主循環函數處理執行用戶命令 -- common/main.c */
                          }
                          }

                          初始化函數序列init_sequence[]
                          init_sequence[]數組保存著基本的初始化函數指針。這些函數名稱和實現的程序文件在下列注釋中。

                          init_fnc_t *init_sequence[] = {
                          cpu_init, /* 基本的處理器相關配置 -- cpu/arm920t/cpu.c */
                          board_init, /* 基本的板級相關配置 -- board/smdk2410/smdk2410.c */
                          interrupt_init, /* 初始化例外處理 -- cpu/arm920t/s3c24x0/interrupt.c */
                          env_init, /* 初始化環境變量 -- common/env_flash.c */
                          init_baudrate, /* 初始化波特率設置 -- lib_arm/board.c */
                          serial_init, /* 串口通訊設置 -- cpu/arm920t/s3c24x0/serial.c */
                          console_init_f, /* 控制臺初始化階段1 -- common/console.c */
                          display_banner, /* 打印u-boot信息 -- lib_arm/board.c */
                          dram_init, /* 配置可用的RAM -- board/smdk2410/smdk2410.c */
                          display_dram_config, /* 顯示RAM的配置大小 -- lib_arm/board.c */
                          NULL,
                          };

                          整個u-boot的執行就進入等待用戶輸入命令,解析并執行命令的死循環中。

                          2、u-boot主要的數據結構

                          u-boot的主要功能是用于引導OS的,但是本身也提供許多強大的功能,可以通過輸入命令行來完成許多操作。所以它本身也是一個很完備的系統。u-boot的大部分操作都是圍繞它自身的數據結構,這些數據結構是通用的,但是不同的板子初始化這些數據就不一樣了。所以u-boot的通用代碼是依賴于這些重要的數據結構的。這里說的數據結構其實就是一些全局變量。
                           1)gd 全局數據變量指針,它保存了u-boot運行需要的全局數據,類型定義:
                           typedef struct global_data {
                          bd_t *bd; //board data pointor板子數據指針
                          unsigned long flags;  //指示標志,如設備已經初始化標志等。
                          unsigned long baudrate; //串口波特率
                          unsigned long have_console; /* 串口初始化標志*/
                          unsigned long reloc_off; /* 重定位偏移,就是實際定向的位置與編譯連接時指定的位置之差,一般為0 */
                          unsigned long env_addr; /* 環境參數地址*/
                          unsigned long env_valid; /* 環境參數CRC檢驗有效標志 */
                          unsigned long fb_base; /* base address of frame buffer */
                           #ifdef CONFIG_VFD
                          unsigned char vfd_type; /* display type */
                           #endif
                          void **jt; /* 跳轉表,1.1.6中用來函數調用地址登記 */
                          } gd_t;
                          2)bd 板子數據指針。板子很多重要的參數。 類型定義如下:
                          typedef struct bd_info {
                          int bi_baudrate; /* 串口波特率 */
                          unsigned long bi_ip_addr; /* IP 地址 */
                          unsigned char bi_enetaddr[6]; /* MAC地址*/
                          struct environment_s *bi_env;
                          ulong bi_arch_number; /* unique id for this board */
                          ulong bi_boot_params; /* 啟動參數 */
                          struct /* RAM 配置 */
                          {
                          ulong start;
                          ulong size;
                          }bi_dram[CONFIG_NR_DRAM_BANKS];
                          } bd_t;
                          3)環境變量指針 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
                           env_ptr指向環境參數區,系統啟動時默認的環境參數environment[],定義在common/environment.c中!
                           參數解釋:
                          bootdelay 定義執行自動啟動的等候秒數
                          baudrate 定義串口控制臺的波特率
                          netmask 定義以太網接口的掩碼
                          ethaddr 定義以太網接口的MAC地址
                          bootfile 定義缺省的下載文件
                          bootargs 定義傳遞給Linux內核的命令行參數
                          bootcmd 定義自動啟動時執行的幾條命令
                          serverip 定義tftp服務器端的IP地址
                          ipaddr 定義本地的IP地址
                          stdin 定義標準輸入設備,一般是串口
                          stdout 定義標準輸出設備,一般是串口
                          stderr 定義標準出錯信息輸出設備,一般是串口
                          4)設備相關:
                          標準IO設備數組evice_t *stdio_devices[] = { NULL, NULL, NULL };
                          設備列表    list_t devlist = 0;
                          device_t的定義:include\devices.h中:
                          typedef struct {
                          int flags;       /* Device flags: input/output/system */
                          int ext;      /* Supported extensions */
                          char name[16];      /* Device name */
                          /* GENERAL functions */
                          int (*start) (void);    /* To start the device */
                          int (*stop) (void);     /* To stop the device */
                          /* 輸出函數 */
                          void (*putc) (const char c); /* To put a char */
                          void (*puts) (const char *s); /* To put a string (accelerator) */
                          /* 輸入函數 */
                          int (*tstc) (void);     /* To test if a char is ready... */
                          int (*getc) (void);     /* To get that char */
                          /* Other functions */
                          void *priv;        /* Private extensions */
                          } device_t;
                           u-boot把可以用為控制臺輸入輸出的設備添加到設備列表devlist,并把當前用作標準IO的設備指針加入stdio_devices數組中。
                           在調用標準IO函數如printf()時將調用stdio_devices數組對應設備的IO函數如putc()。
                          5)命令相關的數據結構,后面介紹。
                          6)與具體設備有關的數據結構,
                           如flash_info_t flash_info[CFG_MAX_FLASH_BANKS];記錄nor flash的信息。
                           nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; nand flash塊設備信息
                          3、u-boot重定位后的內存分布:
                             對于smdk2410,RAM范圍從0x30000000~0x34000000. u-boot占用高端內存區。從高地址到低地址內存分配如下:


                           顯示緩沖區 (.bss_end~34000000)
                          u-boot(bss,data,text) (33f00000~.bss_end)
                          heap(for malloc)
                          gd(global data)
                          bd(board data)
                          stack
                          ....
                          nor flash (0~2M)

                          三、u-boot的重要細節。

                          主要分析流程中各函數的功能。按啟動順序羅列一下啟動函數執行細節。按照函數start_armboot流程進行分析:
                          1)DECLARE_GLOBAL_DATA_PTR;
                          這個宏定義在include/global_data.h中:
                          #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
                          聲明一個寄存器變量 gd 占用r8。這個宏在所有需要引用全局數據指針gd_t *gd的源碼中都有申明。
                          這個申明也避免編譯器把r8分配給其它的變量. 所以gd就是r8,這個指針變量不占用內存。
                          2)gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
                          對全局數據區進行地址分配,_armboot_start為0x3f000000,CFG_MALLOC_LEN是堆大。h境數據區大小,config/smdk2410.h中CFG_MALLOC_LEN大小定義為192KB.
                          3)gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
                          分配板子數據區bd首地址。
                          這樣結合start.s中棧的分配,
                          stack_setup:
                          ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
                          sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
                          sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfoCFG_GBL_DATA_SIZE =128B */
                          #ifdef CONFIG_USE_IRQ
                          sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
                          #endif
                          sub sp, r0, #12 /* leave 3 words for abort-stack */
                          不難得出上文所述的內存分配結構。
                          下面幾個函數是初始化序列表init_sequence[]中的函數:
                          4)cpu_init();定義于cpu/arm920t/cpu.c
                          分配IRQ,FIQ棧底地址,由于沒有定義CONFIG_USE_IRQ,所以相當于空實現。
                          5)board_init;極級初始化,定義于board/smdk2410/smdk2410.c
                           設置PLL時鐘,GPIO,使能I/D cache.
                          設置bd信息:gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;//板子的ID,沒啥意義。
                          gd->bd->bi_boot_params = 0x30000100;//內核啟動參數存放地址
                          6)interrupt_init;定義于cpu/arm920t/s3c24x0/interrupt.c
                           初始化2410的PWM timer 4,使其能自動裝載計數值,恒定的產生時間中斷信號,但是中斷被屏蔽了用不上。
                          7)env_init;定義于common/env_flash.c(搜索的時候發現別的文件也定義了這個函數,而且沒有宏定義保證只有一個被編譯,這是個問題,有高手知道指點一下。
                          功能:指定環境區的地址。default_environment是默認的環境參數設置。
                          gd->env_addr = (ulong)&default_environment[0];
                          gd->env_valid = 0;
                          8)init_baudrate;初始化全局數據區中波特率的值
                          gd->bd->bi_baudrate = gd->baudrate =(i > 0)
                          ? (int) simple_strtoul (tmp, NULL, 10)
                          : CONFIG_BAUDRATE;
                          9)serial_init; 串口通訊設置 定義于cpu/arm920t/s3c24x0/serial.c
                           根據bd中波特率值和pclk,設置串口寄存器。
                          10)console_init_f;控制臺前期初始化common/console.c
                          由于標準設備還沒有初始化(gd->flags & GD_FLG_DEVINIT=0),這時控制臺使用串口作為控制臺
                          函數只有一句:gd->have_console = 1;
                          10)dram_init,初始化內存RAM信息。board/smdk2410/smdk2410.c
                          其實就是給gd->bd中內存信息表賦值而已。
                          gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
                           gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
                           初始化序列表init_sequence[]主要函數分析結束。
                          11)flash_init;定義在board/smdk2410/flash.c
                          這個文件與具體平臺關系密切,smdk2410使用的flash與FS2410不一樣,所以移植時這個程序就得重寫。
                          flash_init()是必須重寫的函數,它做哪些操作呢?
                          首先是有一個變量flash_info_t flash_info[CFG_MAX_FLASH_BANKS]來記錄flash的信息。flash_info_t定義:
                          typedef struct {
                          ulong size; /* 總大小BYTE */
                          ushort sector_count; /* 總的sector數*/
                          ulong flash_id; /* combined device & manufacturer code */
                          ulong start[CFG_MAX_FLASH_SECT]; /* 每個sector的起始物理地址。 */
                          uchar protect[CFG_MAX_FLASH_SECT]; /* 每個sector的保護狀態,如果置1,在執行erase操作的時候將跳過對應sector*/
                          #ifdef CFG_FLASH_CFI //我不管CFI接口。
                          .....
                          #endif
                          } flash_info_t;
                          flash_init()的操作就是讀取ID號,ID號指明了生產商和設備號,根據這些信息設置size,sector_count,flash_id.以及start[]、protect[]。
                          12)把視頻幀緩沖區設置在bss_end后面。
                           addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
                          size = vfd_setmem (addr);
                          gd->fb_base = addr;
                          13)mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
                          設置heap區,供malloc使用。下面的變量和函數定義在lib_arm/board.c
                          malloc可用內存由mem_malloc_start,mem_malloc_end指定。而當前分配的位置則是mem_malloc_brk。
                          mem_malloc_init負責初始化這三個變量。malloc則通過sbrk函數來使用和管理這片內存。
                          static ulong mem_malloc_start = 0;
                          static ulong mem_malloc_end = 0;
                          static ulong mem_malloc_brk = 0;

                          static
                          void mem_malloc_init (ulong dest_addr)
                          {
                          mem_malloc_start = dest_addr;
                          mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
                          mem_malloc_brk = mem_malloc_start;

                          memset ((void *) mem_malloc_start, 0,
                          mem_malloc_end - mem_malloc_start);
                          }
                          void *sbrk (ptrdiff_t increment)
                          {
                          ulong old = mem_malloc_brk;
                          ulong new = old + increment;

                          if ((new < mem_malloc_start) || (new > mem_malloc_end)) {
                          return (NULL);
                          }
                          mem_malloc_brk = new;
                          return ((void *) old);
                          }
                          14)env_relocate() 環境參數區重定位
                          由于初始化了heap區,所以可以通過malloc()重新分配一塊環境參數區,
                          但是沒有必要,因為默認的環境參數已經重定位到RAM中了。
                          /**這里發現個問題,ENV_IS_EMBEDDED是否有定義還沒搞清楚,而且CFG_MALLOC_LEN也沒有定義,也就是說如果ENV_IS_EMBEDDED沒有定義則執行malloc,是不是應該有問題?**/
                          15)IP,MAC地址的初始化。主要是從環境中讀,然后賦給gd->bd對應域就OK。
                          16)devices_init ();定義于common/devices.c
                          int devices_init (void)//我去掉了編譯選項,注釋掉的是因為對應的編譯選項沒有定義。
                          {
                          devlist = ListCreate (sizeof (device_t));//創建設備列表
                          i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);//初始化i2c接口,i2c沒有注冊到devlist中去。
                          //drv_lcd_init ();
                          //drv_video_init ();
                          //drv_keyboard_init ();
                          //drv_logbuff_init ();
                          drv_system_init ();  //這里其實是定義了一個串口設備,并且注冊到devlist中。
                          //serial_devices_init ();
                          //drv_usbtty_init ();
                          //drv_nc_init ();
                          }
                            經過devices_init(),創建了devlist,但是只有一個串口設備注冊在內。顯然,devlist中的設備都是可以做為console的。

                          16) jumptable_init ();初始化gd->jt。1.1.6版本的jumptable只起登記函數地址的作用。并沒有其他作用。
                          17)console_init_r ();后期控制臺初始化
                          主要過程:查看環境參數stdin,stdout,stderr中對標準IO的指定的設備名稱,再按照環境指定的名稱搜索devlist,將搜到的設備指針賦給標準IO數組stdio_devices[]。置gd->flag標志GD_FLG_DEVINIT。這個標志影響putc,getc函數的實現,未定義此標志時直接由串口serial_getc和serial_putc實現,定義以后通過標準設備數組stdio_devices[]中的putc和getc來實現IO。
                          下面是相關代碼:
                          void putc (const char c)
                          {
                          #ifdef CONFIG_SILENT_CONSOLE
                          if (gd->flags & GD_FLG_SILENT)//GD_FLG_SILENT無輸出標志
                          return;
                          #endif
                          if (gd->flags & GD_FLG_DEVINIT) {//設備list已經初始化
                          /* Send to the standard output */
                          fputc (stdout, c);
                          } else {
                          /* Send directly to the handler */
                          serial_putc (c);//未初始化時直接從串口輸出。
                          }
                          }
                          void fputc (int file, const char c)
                          {
                          if (file < MAX_FILES)
                          stdio_devices[file]->putc (c);
                          }

                          為什么要使用devlist,std_device[]?

                          為了更靈活地實現標準IO重定向,任何可以作為標準IO的設備,如USB鍵盤,LCD屏,串口等都可以對應一個device_t的結構體變量,只需要實現getc和putc等函數,就能加入到devlist列表中去,也就可以被assign為標準IO設備std_device中去。如函數

                          int console_assign (int file, char *devname); /* Assign the console 重定向標準輸入輸出*/

                          這個函數功能就是把名為devname的設備重定向為標準IO文件file(stdin,stdout,stderr)。其執行過程是在devlist中查找devname的設備,返回這個設備的device_t指針,并把指針值賦給std_device[file]。


                          18)enable_interrupts(),使能中斷。由于CONFIG_USE_IRQ沒有定義,空實現。
                             #ifdef CONFIG_USE_IRQ
                          /* enable IRQ interrupts */
                          void enable_interrupts (void)
                          {
                          unsigned long temp;
                          __asm__ __volatile__("mrs %0, cpsr\n"
                          "bic %0, %0, #0x80\n"
                          "msr cpsr_c, %0"
                          : "=r" (temp)
                          :
                          : "memory");
                          }
                              #else
                          void enable_interrupts (void)
                          {
                          }
                          19)設置CS8900的MAC地址。
                          cs8900_get_enetaddr (gd->bd->bi_enetaddr);
                          20)初始化以太網。
                          eth_initialize(gd->bd);//bd中已經IP,MAC已經初始化
                          21)main_loop ();定義于common/main.c
                          至此所有初始化工作已經完畢。main_loop在標準轉入設備中接受命令行,然后分析,查找,執行。

                          關于U-boot中命令相關的編程:

                          1、命令相關的函數和定義
                          @main_loop:這個函數里有太多編譯選項,對于smdk2410,去掉所有選項后等效下面的程序
                          void main_loop()
                          {
                          static char lastcommand[CFG_CBSIZE] = { 0, };
                          int len;
                          int rc = 1;
                          int flag;
                          char *s;
                          int bootdelay;
                          s = getenv ("bootdelay"); //自動啟動內核等待延時
                          bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

                          debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
                          s = getenv ("bootcmd"); //取得環境中設置的啟動命令行
                          debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "");

                          if (bootdelay >= 0 && s && !abortboot (bootdelay))
                          {
                          run_command (s, 0);//執行啟動命令行,smdk2410.h中沒有定義CONFIG_BOOTCOMMAND,所以沒有命令執行。
                          }

                          for (;;) {
                          len = readline(CFG_PROMPT);//讀取鍵入的命令行到console_buffer

                          flag = 0; /* assume no special flags for now */
                          if (len > 0)
                          strcpy (lastcommand, console_buffer);//拷貝命令行到lastcommand.
                          else if (len == 0)
                          flag |= CMD_FLAG_REPEAT;
                          if (len == -1)
                          puts ("\n");
                          else
                          rc = run_command (lastcommand, flag); //執行這個命令行。

                          if (rc <= 0) {
                          /* invalid command or not repeatable, forget it */
                          lastcommand[0] = 0;
                          }
                          }
                          。纑un_comman();在命令table中查找匹配的命令名稱,得到對應命令結構體變量指針,以解析得到的參數調用其處理函數執行命令。
                          @命令結構構體類型定義:command.h中,
                          struct cmd_tbl_s {
                          char *name; /* 命令名 */
                          int maxargs; /* 大參數個數maximum number of arguments */
                          int repeatable; /* autorepeat allowed? */
                          /* Implementation function 命令執行函數*/
                          int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
                          char *usage; /* Usage message (short) */
                          #ifdef CFG_LONGHELP
                          char *help; /* Help message (long) */
                          #endif
                          #ifdef CONFIG_AUTO_COMPLETE
                          /* do auto completion on the arguments */
                          int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
                          #endif
                          };
                          typedef struct cmd_tbl_s cmd_tbl_t;


                          //定義section屬性的結構體。編譯的時候會單獨生成一個名為.u_boot_cmd的section段。
                          #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))


                          //這個宏定義一個命令結構體變量。并用name,maxargs,rep,cmd,usage,help初始化各個域。
                          #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
                          cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

                          2、在u-boot中,如何添加一個命令:
                          1)CFG_CMD_* 命令選項位標志。在include/cmd_confdefs.h 中定義。
                          每個板子的配置文件(如include/config/smdk2410.h)中都可以定義u-boot
                          需要的命令,如果要添加一個命令,必須添加相應的命令選項。如下:
                          #define CONFIG_COMMANDS \
                          (CONFIG_CMD_DFL | \
                          CFG_CMD_CACHE | \
                          /*CFG_CMD_NAND |*/ \
                          /*CFG_CMD_EEPROM |*/ \
                          /*CFG_CMD_I2C |*/ \
                          /*CFG_CMD_USB |*/ \
                          CFG_CMD_REGINFO | \
                          CFG_CMD_DATE | \
                          CFG_CMD_ELF)
                          定義這個選項主要是為了編譯命令需要的源文件,大部分命令都在common文件夾下對應一個源文件
                          cmd_*.c ,如cmd_cache.c實現cache命令!∥募_頭就有一行編譯條件:
                          #if(CONFIG_COMMANDS&CFG_CMD_CACHE)
                          也就是說,如果配置頭文件中CONFIG_COMMANDS不或上相應命令的選項,這里就不會被編譯。
                           2)定義命令結構體變量,如:
                            U_BOOT_CMD(
                          dcache, 2, 1, do_dcache,
                          "dcache - enable or disable data cache\n",
                          "[on, off]\n"
                          " - enable or disable data (writethrough) cache\n"
                          );
                           其實就是定義了一個cmd_tbl_t類型的結構體變量,這個結構體變量名為__u_boot_cmd_dcache。
                          其中變量的五個域初始化為括號的內容。分別指明了命令名,參數個數,重復數,執行命令的函數,命令提示。
                          每個命令都對應這樣一個變量,同時這個結構體變量的section屬性為.u_boot_cmd.也就是說每個變量編譯結束
                          在目標文件中都會有一個.u_boot_cmd的section.一個section是連接時的一個輸入段,如.text,.bss,.data等都是section名。
                          后由鏈接程序把所有的.u_boot_cmd段連接在一起,這樣就組成了一個命令結構體數組。
                          u-boot.lds中相應腳本如下:
                          . = .;
                          __u_boot_cmd_start = .;
                          .u_boot_cmd : { *(.u_boot_cmd) }
                          __u_boot_cmd_end = .;
                          可以看到所有的命令結構體變量集中在__u_boot_cmd_start開始到__u_boot_cmd_end結束的連續地址范圍內,
                          這樣形成一個cmd_tbl_t類型的數組,run_command函數就是在這個數組中查找命令的。
                          3)實現命令處理函數。命令處理函數的格式:
                          void function (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

                          總體來說,如果要實現自己的命令,應該在include/com_confdefs.h中定義一個命令選項標志位。
                          在板子的配置文件中添加命令自己的選項。按照u-boot的風格,可以在common/下面添加自己的cmd_*.c,并且定義自己的命令結構體變量,如U_BOOT_CMD(
                          mycommand, 2, 1, do_mycommand,
                          "my command!\n",
                          "...\n"
                          " ..\n"
                          );

                          然后實現自己的命令處理函數do_mycommand(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])。

                          四、U-boot在ST2410的移植,基于NOR FLASH和NAND FLASH啟動。
                          1、從smdk2410到ST2410:
                          ST2410板子的核心板與FS2410是一樣的。我沒有整到smdk2410的原理圖,從網上得知的結論總結如下,
                          fs2410與smdk2410 RAM地址空間大小一致(0x30000000~0x34000000=64MB);

                          NOR FLASH型號不一樣,FS2410用SST39VF1601系列的,smdk2410用AMD產LV系列的;

                          網絡芯片型號和在內存中映射的地址完全一致(CS8900,IO方式基地址0x19000300)


                          2、移植過程:
                          移植u-boot的基本步驟如下
                          (1) 在頂層Makefile中為開發板添加新的配置選項,使用已有的配置項目為例。
                          smdk2410_config : unconfig
                          @./mkconfig $(@:_config=) arm arm920t smdk2410 NULL s3c24×0
                          參考上面2行,添加下面2行。
                          fs2410_config : unconfig
                          @./mkconfig $(@:_config=) arm arm920t fs2410 NULL s3c24×0

                          (2) 創建一個新目錄存放開發板相關的代碼,并且添加文件。
                          board/fs2410/config.mk
                          board/fs2410/flash.c
                          board/fs2410/fs2410.c
                          board/fs2410/Makefile
                          board/fs2410/memsetup.S
                          board/fs2410/u-boot.lds
                          注意將board/fs2410/Makefile中smdk2410.o全部改為fs2410.o
                          (3) 為開發板添加新的配置文件
                          可以先復制參考開發板的配置文件,再修改。例如:
                          $cp include/configs/smdk2410.h include/configs/fs2410.h
                          如果是為一顆新的CPU移植,還要創建一個新的目錄存放CPU相關的代碼。

                          (4) 配置開發板
                          $ make fs2410_config

                          3、移植要考慮的問題:
                           從smdk2410到ST2410移植要考慮的主要問題就是NOR flash。從上述分析知道,u-boot啟動時要執行flash_init() 檢測flash的ID號,大小,secotor起始地址表和保護狀態表,這些信息全部保存在flash_info_t flash_info[CFG_MAX_FLASH_BANKS]中。
                            另外,u-boot中有一些命令如saveenvt需要要擦寫flash,間接調用兩個函數:flash_erase和write_buff。在board/smdk2410/flash.c
                          實現了與smdk2410板子相關的nor flash函數操作。由于write_buffer中調用了write_hword去具體寫入一個字到flash中,這個函數本身是與硬件無關的,
                          所以與硬件密切相關的三個需要重寫的函數是flash_init, flash_erase,write_hword;
                          4、SST39VF1601:
                          FS2410板nor flash型號是SST39VF1601,根據data sheet,其主要特性如下:
                          16bit字為訪問單位。2MBTYE大小。
                          sector大小2kword=4KB,block大小32Kword=64KB;這里我按block為單位管理flash,即flash_info結構體變量中的sector_count是block數,起始地址表保存也是所有block的起始地址。
                          SST Manufacturer ID = 00BFH ;
                          SST39VF1601 Device ID = 234BH;
                          軟件命令序列如下圖。


                          5、我實現的flash.c主要部分:

                          //相關定義:
                          # define CFG_FLASH_WORD_SIZE unsigned short //訪問單位為16b字
                          #define MEM_FLASH_ADDR1 (*(volatile CFG_FLASH_WORD_SIZE *)(CFG_FLASH_BASE + 0x000005555<<1 ))

                          //命令序列地址1,由于2410地址線A1與SST39VF1601地址線A0連接實現按字訪問,因此這個地址要左移1位。
                          #define MEM_FLASH_ADDR2 (*(volatile CFG_FLASH_WORD_SIZE *)(CFG_FLASH_BASE + 0x000002AAA<<1 )) //命令序列地址2
                          #define READ_ADDR0 (*(volatile CFG_FLASH_WORD_SIZE *)(CFG_FLASH_BASE + 0x0000))

                          //flash信息讀取地址1,A0=0,其余全為0
                          #define READ_ADDR1 (*(volatile CFG_FLASH_WORD_SIZE *)(CFG_FLASH_BASE + 0x0001<<1)) //flash信息讀取地址2,A0=1,其余全為0
                          flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* 定義全局變量flash_info[1]*/

                          //flash_init(),我實現的比較簡單,因為是與板子嚴重依賴的,只要檢測到的信息與板子提供的已知信息符合就OK。
                          ulong flash_init (void)
                          {
                          int i;

                          CFG_FLASH_WORD_SIZE value;
                          flash_info_t *info;
                          for (i = 0; i < CFG_MAX_FLASH_BANKS; i++)
                          {
                          flash_info[i].flash_id=FLASH_UNKNOWN;
                          }
                          info=(flash_info_t *)(&flash_info[0]);

                          //進入讀ID狀態,讀MAN ID和device id
                          MEM_FLASH_ADDR1=(CFG_FLASH_WORD_SIZE)(0x00AA);
                          MEM_FLASH_ADDR2=(CFG_FLASH_WORD_SIZE)(0x0055);
                          MEM_FLASH_ADDR1=(CFG_FLASH_WORD_SIZE)(0x0090);

                          value=READ_ADDR0; //read Manufacturer ID

                          if(value==(CFG_FLASH_WORD_SIZE)SST_MANUFACT)
                          info->flash_id = FLASH_MAN_SST;
                          else
                          {
                          panic("NOT expected FLASH FOUND!\n");return 0;
                          }
                          value=READ_ADDR1; //read device ID

                          if(value==(CFG_FLASH_WORD_SIZE)SST_ID_xF1601)
                          {
                          info->flash_id += FLASH_SST1601;
                          info->sector_count = 32; //32 block
                          info->size = 0x00200000; // 2M=32*64K
                          }
                          else
                          {
                          panic("NOT expected FLASH FOUND!\n");return 0;
                          }

                          //建立sector起始地址表。
                          if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST )
                          {
                          for (i = 0; i < info->sector_count; i++)
                          info->start[i] = CFG_FLASH_BASE + (i * 0x00010000);
                          }

                          //設置sector保護信息,對于SST生產的FLASH,全部設為0。
                          for (i = 0; i < info->sector_count; i++)
                          {
                          if((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST)
                          info->protect[i] = 0;
                          }

                          //結束讀ID狀態:
                          *((CFG_FLASH_WORD_SIZE *)&info->start[0])= (CFG_FLASH_WORD_SIZE)0x00F0;

                          //設置保護,將u-boot鏡像和環境參數所在的block的proctect標志置1
                          flash_protect (FLAG_PROTECT_SET,
                          CFG_FLASH_BASE,
                          CFG_FLASH_BASE + monitor_flash_len - 1,
                          &flash_info[0]);

                          flash_protect (FLAG_PROTECT_SET,
                          CFG_ENV_ADDR,
                          CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]);
                          return info->size;
                          }
                             
                          //flash_erase實現
                           這里給出修改的部分,s_first,s_last是要擦除的block的起始和終止block號.對于protect[]置位的block不進行擦除。
                          擦除一個block命令時序按照上面圖示的Block-Erase進行。
                          for (sect = s_first; sect<=s_last; sect++)
                          {
                          if (info->protect[sect] == 0)
                          { /* not protected */
                          addr = (CFG_FLASH_WORD_SIZE *)(info->start[sect]);
                          if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST)
                          {
                          MEM_FLASH_ADDR1 = (CFG_FLASH_WORD_SIZE)0x00AA;
                          MEM_FLASH_ADDR2 = (CFG_FLASH_WORD_SIZE)0x0055;
                          MEM_FLASH_ADDR1 = (CFG_FLASH_WORD_SIZE)0x0080;
                          MEM_FLASH_ADDR1 = (CFG_FLASH_WORD_SIZE)0x00AA;
                          MEM_FLASH_ADDR2 = (CFG_FLASH_WORD_SIZE)0x0055;
                          addr[0] = (CFG_FLASH_WORD_SIZE)0x0050; /* block erase */
                          for (i=0; i<50; i++)
                          udelay(1000); /* wait 1 ms */
                          }
                          else
                          {
                          break;
                          }
                          }
                          }
                          .........
                          start = get_timer (0);  //在指定時間內不能完成為超時。
                          last = start;
                          addr = (CFG_FLASH_WORD_SIZE *)(info->start[l_sect]);//查詢DQ7是否為1,DQ7=1表明擦除完畢
                          while ((addr[0] & (CFG_FLASH_WORD_SIZE)0x0080) != (CFG_FLASH_WORD_SIZE)0x0080) {
                          if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) {
                          printf ("Timeout\n");
                          return 1;
                          }
                          ................

                          //write_word操作,這個函數由write_buff一調用,完成寫入一個word的操作,其操作命令序列由上圖中Word-Program指定。
                          static int write_word (flash_info_t *info, ulong dest, ulong data)
                          {
                          volatile CFG_FLASH_WORD_SIZE *dest2 = (CFG_FLASH_WORD_SIZE *)dest;
                          volatile CFG_FLASH_WORD_SIZE *data2 = (CFG_FLASH_WORD_SIZE *)&data;
                          ulong start;
                          int flag;
                          int i;

                          /* Check if Flash is (sufficiently) erased */
                          if ((*((volatile ulong *)dest) & data) != data) {
                          return (2);
                          }
                          /* Disable interrupts which might cause a timeout here */
                          flag = disable_interrupts();

                          for (i=0; i<4/sizeof(CFG_FLASH_WORD_SIZE); i++)
                          {
                          MEM_FLASH_ADDR1 = (CFG_FLASH_WORD_SIZE)0x00AA;
                          MEM_FLASH_ADDR2 = (CFG_FLASH_WORD_SIZE)0x0055;
                          MEM_FLASH_ADDR1 = (CFG_FLASH_WORD_SIZE)0x00A0;

                          dest2[i] = data2[i];

                          /* re-enable interrupts if necessary */
                          if (flag)
                          enable_interrupts();

                          /* data polling for D7 */
                          start = get_timer (0);
                          while ((dest2[i] & (CFG_FLASH_WORD_SIZE)0x0080) !=
                          (data2[i] & (CFG_FLASH_WORD_SIZE)0x0080)) {
                          if (get_timer(start) > CFG_FLASH_WRITE_TOUT) {
                          return (1);
                          }
                          }
                          }
                          return (0);
                          }

                          這些代碼在與nor flash相關的命令中都會間接被調用。所以u-boot可移植性的另一個方面就是規定一些函數調用接口和全局變量,這些函數的實現是硬件相關的,移植時只需要實現這些函數。
                          而全局變量是具體硬件無關的。u-boot在通用目錄中實現其余與硬件無關的函數,這些函數就只與全局變量和函數接口打交道了。 通過編譯選項設置來靈活控制是否需要編譯通用部分。


                          6、增加從Nand 啟動的代碼:
                          FS2410板有跳線,跳線短路時從NAND啟動,否則從NOR啟動。根據FS2410 BIOS源碼,我修改了start.s加入了可以從兩種FLASH中啟動u-boot的
                          代碼。原理在于:在重定位之前先讀BWSCON寄存器,判斷OM0位是0(有跳線,NAND啟動)還是1(無跳線,NOR啟動),采取不同的重定位代碼
                          分別從nand或nor中拷貝u-boot鏡像到RAM中。這里面也有問題,比如從Nand啟動后,nor flash的初始化代碼和與它相關的命令都是不能使用的。
                          這里我采用比較簡單的方法,定義一個全局變量標志_boot_flash保存當前啟動FLASH標志,_boot_flash=0則表明是NOR啟動,否則是從NAND。
                          在每個與nor flash 相關的命令執行函數一開始就判斷這個變量,如果為1立即返回。flash_init()也必須放在這個if(!_boot_flash)條件中。
                          這里方法比較笨,主要是為了能在跳線處于任意狀態時都能啟動u-boot。
                          修改后的start.s如下。
                          .......
                          //修改1
                          .globl _boot_flash
                          _boot_flash: //定義全局標志變量,0:NOR FLASH啟動,1:NAND FLASH啟動。
                          .word 0x00000000
                          .........

                          ///修改2:

                          ldr r0,=BWSCON
                          ldr r0,[r0]
                          ands r0,r0,#6
                          beq nand_boot //OM0=0,有跳線,從Nand啟動。nand_boot在后面定義。
                          ............

                          //修改4,這里在全局變量_boot_flash中設置當前啟動flash設備是NOR還是NAND
                          //這里已經完成搬運到RAM的工作,即將跳轉到RAM中_start_armboot函數中執行。
                          adr r1,_boot_flash //取_boot_flash的當前地址,這時還在NOR FLASH或者NAND 4KB緩沖中。
                          ldr r2,_TEXT_BASE
                          add r1,r1,r2 //得到_boot_flash重定位后的地址,這個地址在RAM中。
                          ldr r0,=BWSCON
                          ldr r0,[r0]
                          ands r0,r0,#6 //
                          mov r2,#0x00000001
                          streq r2,[r1] //如果當前是從NAND啟動,置_boot_flash為1

                          ldr pc, _start_armboot

                          _start_armboot: .word start_armboot

                          ........

                          //////// 修改4,從NAND拷貝U-boot鏡像(大128KB),這段代碼由fs2410 BIOS修改得來。
                          nand_boot:
                          mov r5, #NFCONF
                          ldr r0, =(1<<15)|(1<<12)|(1<<11)|(7<<8)|(7<<4)|(7)
                          str r0, [r5]

                          bl ReadNandID
                          mov r6, #0
                          ldr r0, =0xec73
                          cmp r5, r0
                          beq x1
                          ldr r0, =0xec75
                          cmp r5, r0
                          beq x1
                          mov r6, #1
                          x1:
                          bl ReadNandStatus

                          mov r8, #0 //r8是PAGE數變量
                          ldr r9, _TEXT_BASE //r9指向u-boot在RAM中的起始地址。
                          x2:
                          ands r0, r8, #0x1f
                          bne x3 //此處意思在于頁數是32的整數倍的時候才進行一次壞塊檢查 1 block=32 pages,否則直接讀取頁面。
                          mov r0, r8
                          bl CheckBadBlk //檢查壞塊返回值非0表明當前塊不是壞塊。
                          cmp r0, #0
                          addne r8, r8, #32 //如果當前塊壞了,跳過讀取操作。 1 block=32 pages
                          bne x4
                          x3:
                          mov r0, r8
                          mov r1, r9
                          bl ReadNandPage //讀取一頁(512B)
                          add r9, r9, #512
                          add r8, r8, #1
                          x4:
                          cmp r8, #256 //一共讀取256*512=128KB。
                          bcc x2

                          mov r5, #NFCONF //DsNandFlash
                          ldr r0, [r5]
                          and r0, r0, #~0x8000
                          str r0, [r5]

                          adr lr,stack_setup //注意這里直接跳轉到stack_setup中執行
                          mov pc,lr
                          ///
                          /*************************************************
                          *
                          *Nand basic functions:
                          *************************************************
                          */

                          //讀取Nand的ID號,返回值在r5中
                          ReadNandID:
                          mov r7,#NFCONF
                          ldr r0,[r7,#0] //NFChipEn();
                          bic r0,r0,#0x800
                          str r0,[r7,#0]
                          mov r0,#0x90 //WrNFCmd(RdIDCMD);
                          strb r0,[r7,#4]
                          mov r4,#0 //WrNFAddr(0);
                          strb r4,[r7,#8]
                          y1: //while(NFIsBusy());
                          ldr r0,[r7,#0x10]
                          tst r0,#1
                          beq y1
                          ldrb r0,[r7,#0xc] //id = RdNFDat()<<8;
                          mov r0,r0,lsl #8
                          ldrb r1,[r7,#0xc] //id |= RdNFDat();
                          orr r5,r1,r0
                          ldr r0,[r7,#0] //NFChipDs();
                          orr r0,r0,#0x800
                          str r0,[r7,#0]
                          mov pc,lr

                          //讀取Nand狀態,返回值在r1,此處沒有用到返回值。

                          ReadNandStatus:
                          mov r7,#NFCONF
                          ldr r0,[r7,#0] //NFChipEn();
                          bic r0,r0,#0x800
                          str r0,[r7,#0]
                          mov r0,#0x70 //WrNFCmd(QUERYCMD);
                          strb r0,[r7,#4]
                          ldrb r1,[r7,#0xc] //r1 = RdNFDat();
                          ldr r0,[r7,#0] //NFChipDs();
                          orr r0,r0,#0x800
                          str r0,[r7,#0]
                          mov pc,lr

                          //等待Nand內部操作完畢
                          WaitNandBusy:
                          mov r0,#0x70 //WrNFCmd(QUERYCMD);
                          mov r1,#NFCONF
                          strb r0,[r1,#4]
                          z1: //while(!(RdNFDat()&0x40));
                          ldrb r0,[r1,#0xc]
                          tst r0,#0x40
                          beq z1
                          mov r0,#0 //WrNFCmd(READCMD0);
                          strb r0,[r1,#4]
                          mov pc,lr

                          //檢查壞block:
                          CheckBadBlk:
                          mov r7, lr
                          mov r5, #NFCONF

                          bic r0, r0, #0x1f //addr &= ~0x1f;
                          ldr r1,[r5,#0] //NFChipEn()
                          bic r1,r1,#0x800
                          str r1,[r5,#0]

                          mov r1,#0x50 //WrNFCmd(READCMD2)
                          strb r1,[r5,#4]
                          mov r1, #6
                          strb r1,[r5,#8] //WrNFAddr(6)
                          strb r0,[r5,#8] //WrNFAddr(addr)
                          mov r1,r0,lsr #8 //WrNFAddr(addr>>8)
                          strb r1,[r5,#8]
                          cmp r6,#0 //if(NandAddr)
                          movne r0,r0,lsr #16 //WrNFAddr(addr>>16)
                          strneb r0,[r5,#8]

                          bl WaitNandBusy //WaitNFBusy()

                          ldrb r0, [r5,#0xc] //RdNFDat()
                          sub r0, r0, #0xff

                          mov r1,#0 //WrNFCmd(READCMD0)
                          strb r1,[r5,#4]

                          ldr r1,[r5,#0] //NFChipDs()
                          orr r1,r1,#0x800
                          str r1,[r5,#0]

                          mov pc, r7

                          ReadNandPage:
                          mov r7,lr
                          mov r4,r1
                          mov r5,#NFCONF

                          ldr r1,[r5,#0] //NFChipEn()
                          bic r1,r1,#0x800
                          str r1,[r5,#0]

                          mov r1,#0 //WrNFCmd(READCMD0)
                          strb r1,[r5,#4]
                          strb r1,[r5,#8] //WrNFAddr(0)
                          strb r0,[r5,#8] //WrNFAddr(addr)
                          mov r1,r0,lsr #8 //WrNFAddr(addr>>8)
                          strb r1,[r5,#8]
                          cmp r6,#0 //if(NandAddr)
                          movne r0,r0,lsr #16 //WrNFAddr(addr>>16)
                          strneb r0,[r5,#8]

                          ldr r0,[r5,#0] //InitEcc()
                          orr r0,r0,#0x1000
                          str r0,[r5,#0]

                          bl WaitNandBusy //WaitNFBusy()

                          mov r0,#0 //for(i=0; i<512; i++)
                          r1:
                          ldrb r1,[r5,#0xc] //buf[i] = RdNFDat()
                          strb r1,[r4,r0]
                          add r0,r0,#1
                          bic r0,r0,#0x10000
                          cmp r0,#0x200
                          bcc r1

                          ldr r0,[r5,#0] //NFChipDs()
                          orr r0,r0,#0x800
                          str r0,[r5,#0]

                          mov pc,r7

                          關于nand命令,我嘗試打開CFG_CMD_NAND選項,并定義
                          #define CFG_MAX_NAND_DEVICE 1
                          #define MAX_NAND_CHIPS 1
                          #define CFG_NAND_BASE 0x4e000000
                          添加boar_nand_init()定義(空實現)。但是連接時出現問題,原因是u-boot使用的是軟浮點,而我的交叉編譯arm-linux-gcc是硬件浮點。
                          看過一些解決方法,比較麻煩,還沒有解決這個問題,希望好心的高手指點。不過我比較納悶,u-boot在nand部分哪里會用到浮點運算呢?

                          7、添加網絡命令。
                          我嘗試使用ping命令,其余的命令暫時不考慮。
                          在common/cmd_net中,首先有條件編譯 #if (CONFIG_COMMANDS & CFG_CMD_NET),然后在命令函數do_ping(...)定義之前有條件編譯判斷
                          #if (CONFIG_COMMANDS & CFG_CMD_PING) 。所以在include/cofig/fs2410.h中必須打開這兩個命令選項。
                          #define CONFIG_COMMANDS \
                          (CONFIG_CMD_DFL | \
                          CFG_CMD_CACHE | \
                          CFG_CMD_REGINFO | \
                          CFG_CMD_DATE | \
                          CFG_CMD_NET | \ //
                          CFG_CMD_PING |\ //
                          CFG_CMD_ELF)
                          并且設定IP:192.168.0.12。

                          至此,整個移植過程已經完成。編譯連接生成u-boot.bin,燒到nand 和nor上都能順利啟動u-boot,使用ping命令時出現問題,
                          發現ping自己的主機竟然超時,還以為是程序出了問題,后來才發現是windows防火墻的問題。關閉防火墻就能PING通了。

                          總體來說,u-boot是一個很特殊的程序,代碼龐大,功能強大,自成體系。為了在不同的CPU,ARCH,BOARD上移植進行了很多靈活的設計。

                           
                           
                          版權所有:曙海信息網絡科技有限公司 copyright 2000-2016
                           
                          上?偛颗嘤柣

                          地址:上海市云屏路1399號26#新城金郡商務樓310。
                          (地鐵11號線白銀路站2號出口旁,云屏路和白銀路交叉口)
                          郵編:201821
                          熱線:021-51875830 32300767
                          傳真:021-32300767
                          業務手機:15921673576/13918613812
                          E-mail:officeoffice@126.com
                          客服QQ: 849322415
                          北京培訓基地

                          地址:北京市昌平區沙河南街11號312室
                          (地鐵昌平線沙河站B出口) 郵編:102200 行走路線:請點擊這查看!
                          熱線:010-51292078
                          傳真:010-51292078
                          業務手機:15701686205
                          客服QQ:1243285887
                          成都培訓基地

                          地址:四川省成都市高新區中和大道一段99號領館區1號1-3-2903 郵編:610031
                          熱線:4008699035 業務手機:13540421960
                          客服QQ:1325341129
                          南京培訓基地

                          地址:江蘇省南京市棲霞區和燕路251號金港大廈B座2201室
                          (地鐵一號線邁皋橋站1號出口旁,近南京火車站)
                          熱線:4008699035
                          傳真:4008699035
                          郵編:210046
                          客服QQ:1325341129
                           
                          深圳培訓基地

                          地址:深圳市環觀中路28號82#201室

                          熱線:4008699035
                          傳真:4008699035
                          業務手機:13699831341

                          郵編:518001
                          客服QQ:2472106501
                          武漢培訓基地

                          地址:湖北省武漢市東湖高新技術開發區高新二路128號 佳源大廈一期A4-1-701 郵編:430022
                          熱線:4008699035
                          客服QQ:849322415
                          廣州培訓基地

                          地址:廣州市越秀區環市東路486號廣糧大廈1202室

                          熱線:4008699035
                          傳真:4008699035

                          郵編:510075
                          石家莊培訓基地

                          地址:石家莊市高新區中山東路618號瑞景大廈1#802

                          熱線:4008699035
                          業務手機:13933071028
                          傳真:4008699035
                          郵編:050200
                           
                          沈陽培訓基地

                          地址:遼寧省沈陽市東陵渾南新區沈營路六宅臻品29-11-9 郵編:110179
                          熱線:4008699035
                          鄭州培訓基地

                          地址:鄭州市高新區雪松路錦華大廈401

                          熱線:4008699035

                          郵編:450001
                          西安培訓基地

                          地址:西安市雁塔區高新二路12號協同大廈901室

                          熱線:029-86699670
                          業務手機:18392016509
                          傳真:029-86699670
                          郵編:710054
                           

                          雙休日、節假日及晚上可致電值班電話:021-51875830 值班手機:15921673576/13918613812


                          備案號:滬ICP備08026168號

                          .(2014年7月11)..................................................................................................
                          友情鏈接:Cadence培訓 ICEPAK培訓 EMC培訓 電磁兼容培訓 sas容培訓 羅克韋爾PLC培訓 歐姆龍PLC培訓 PLC培訓 三菱PLC培訓 西門子PLC培訓 dcs培訓 橫河dcs培訓 艾默生培訓 robot CAD培訓 eplan培訓 dcs培訓 電路板設計培訓 浙大dcs培訓 PCB設計培訓 adams培訓 fluent培訓系列課程 培訓機構課程短期培訓系列課程培訓機構 長期課程列表實踐課程高級課程學校培訓機構周末班培訓 南京 短期培訓系列課程培訓機構 長期課程列表實踐課程高級課程學校培訓機構周末班 曙海 教育 企業 培訓課程 系列班 長期課程列表實踐課程高級課程學校培訓機構周末班 短期培訓系列課程培訓機構 曙海教育企業培訓課程 系列班
                          在線客服
                          丰满的熟妇露脸大屁股 香蕉在线 亚洲 欧美 专区| 2012国语在线看免费观看| 手机在线超碰网| 熟女体下毛毛黑森林| 和风韵犹存的熟妇乱爱| 一线高清视频在线| 一下课男生就把我拉进厕所| 大炕上的肉体交换| japanese色系page强奷gay| 国产小屁孩cao大人| xxxxbbbb欧美| 我和一个三十少妇| 自拍偷自拍亚洲精品第1页| freesexvideos性亚洲| 在线看片免费不卡人成视频| 中国xxxx片免费| 少妇老师寂寞难耐高潮电影| 拍拍拍无挡免费视频| 无码黄动漫在线观看| 中国xxxx片免费| 国产成人18黄网站| 男人把女人桶爽30分钟| 亚洲日韩中文字幕日本有码| yw193.com| 强奷绝色年轻女教师| 满床春水BD| gogo全球专业大尺度高清人体| 久久国产乱子伦精品免费女| naruto hentai| vide0sdesext姑娘| 国产老司机福利视频网| http://www.jikjijikjijikji.com