<thead id="hntrp"><optgroup id="hntrp"></optgroup></thead>

    1. <i id="hntrp"><meter id="hntrp"></meter></i>
          <thead id="hntrp"><optgroup id="hntrp"></optgroup></thead>
           
            Home  手機訪問   課程介紹   培訓報名  企業(yè)培訓   付款方式   講師介紹   學員評價  曙海簡介  聯(lián)系曙海  工程承接  
          arduino培訓
          嵌入式協(xié)處理器--DSP
          嵌入式協(xié)處理器--FPGA
          FPGA項目實戰(zhàn)系列課程----
          嵌入式OS--3G手機操作系統(tǒng)
          嵌入式OS-Linux
          嵌入式CPU--ARM

          嵌入式OS--WinCE
          單片機培訓
          嵌入式硬件設計
          嵌入式OS--VxWorks
          學員VIP專區(qū)
          用戶名:

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

          值班QQ:
          點擊這里給我發(fā)消息

          值班網(wǎng)頁在線客服,點擊交談:
           
          網(wǎng)頁在線客服

           
          合作伙伴與授權機構
          現(xiàn)代化的多媒體教室
          FPGA培訓

          合作企業(yè)最新人才需求公告

          ◆招人、應聘、人才合作
          請訪問曙海旗下網(wǎng)站---

          電子人才網(wǎng)
          www.morning-sea.com.cn
          合作企業(yè)最新人才需求公告
          奧迪堅通訊系統(tǒng)(上海)有限公司
          (16人)
          上海貝爾阿爾卡特股份有限公司(12人) 
          冠捷半導體(上海)有限公司
          (9人)
          上海奧波電子有限公司(14人)
          北京華路時代信息技術有限公司
          (18人)
          上海寶康電子控制工程有限公司
          (6人)
          上海迅時通信設備有限公司(8人)
          上海天能電子有限公司(5人)
          公益培訓
          曙海動態(tài)
           
          郵件訂閱列表
           


           

            FPGA培訓 

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

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

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

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

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

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

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

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

          頂層makefile先調(diào)用各子目錄的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_,用來選擇處理器、設備接口、命令、屬性等,主要用來 決定是否編譯某些文件或者函數(shù)。

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

          這兩類宏定義對u-boot的移植性非常關鍵,比如drive/CS8900.c,對cs8900而言,很多操作都是通用的,但不是所有的板子上面都有這個芯片,即使有它在內(nèi)存中映射的基地址也是平臺相關的。所以對于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中在函數(shù)定義的前面就有編譯條件判斷:#ifdef CONFIG_DRIVER_CS8900 如果這個選項沒有定義,整個cs8900.c就不會被編譯了。
          而常數(shù)參量CS8900_BASE則用在cs8900.h頭文件中定義各個功能寄存器的地址。u-boot的CS8900工作在IO模式下,只要給定IO寄存器在內(nèi)存中映射的基地址,其余代碼就與平臺無關了。

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

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

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

          /* 給板子數(shù)據(jù)變量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的長度。

          /* 順序執(zhí)行init_sequence數(shù)組中的初始化函數(shù) */
          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);
          /* 重新定位環(huán)境變量, */
          env_relocate ();
          /* 從環(huán)境變量中獲取IP地址 */
          gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
          /* 以太網(wǎng)接口MAC 地址 */
          ……
          devices_init (); /* 設備初始化 */
          jumptable_init (); //跳轉(zhuǎn)表初始化
          console_init_r (); /* 完整地初始化控制臺設備 */
          enable_interrupts (); /* 使能中斷處理 */
          /* 通過環(huán)境變量初始化 */
          if ((s = getenv ("loadaddr")) != NULL) {
          load_addr = simple_strtoul (s, NULL, 16);
          }
          /* main_loop()循環(huán)不斷執(zhí)行 */
          for (;;) {
          main_loop (); /* 主循環(huán)函數(shù)處理執(zhí)行用戶命令 -- common/main.c */
          }
          }

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

          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, /* 初始化環(huán)境變量 -- 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的執(zhí)行就進入等待用戶輸入命令,解析并執(zhí)行命令的死循環(huán)中。

          2、u-boot主要的數(shù)據(jù)結構

          u-boot的主要功能是用于引導OS的,但是本身也提供許多強大的功能,可以通過輸入命令行來完成許多操作。所以它本身也是一個很完備的系統(tǒng)。u-boot的大部分操作都是圍繞它自身的數(shù)據(jù)結構,這些數(shù)據(jù)結構是通用的,但是不同的板子初始化這些數(shù)據(jù)就不一樣了。所以u-boot的通用代碼是依賴于這些重要的數(shù)據(jù)結構的。這里說的數(shù)據(jù)結構其實就是一些全局變量。
           1)gd 全局數(shù)據(jù)變量指針,它保存了u-boot運行需要的全局數(shù)據(jù),類型定義:
           typedef struct global_data {
          bd_t *bd; //board data pointor板子數(shù)據(jù)指針
          unsigned long flags;  //指示標志,如設備已經(jīng)初始化標志等。
          unsigned long baudrate; //串口波特率
          unsigned long have_console; /* 串口初始化標志*/
          unsigned long reloc_off; /* 重定位偏移,就是實際定向的位置與編譯連接時指定的位置之差,一般為0 */
          unsigned long env_addr; /* 環(huán)境參數(shù)地址*/
          unsigned long env_valid; /* 環(huán)境參數(shù)CRC檢驗有效標志 */
          unsigned long fb_base; /* base address of frame buffer */
           #ifdef CONFIG_VFD
          unsigned char vfd_type; /* display type */
           #endif
          void **jt; /* 跳轉(zhuǎn)表,1.1.6中用來函數(shù)調(diào)用地址登記 */
          } gd_t;
          2)bd 板子數(shù)據(jù)指針。板子很多重要的參數(shù)。 類型定義如下:
          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; /* 啟動參數(shù) */
          struct /* RAM 配置 */
          {
          ulong start;
          ulong size;
          }bi_dram[CONFIG_NR_DRAM_BANKS];
          } bd_t;
          3)環(huán)境變量指針 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
           env_ptr指向環(huán)境參數(shù)區(qū),系統(tǒng)啟動時默認的環(huán)境參數(shù)environment[],定義在common/environment.c中。 
           參數(shù)解釋:
          bootdelay 定義執(zhí)行自動啟動的等候秒數(shù)
          baudrate 定義串口控制臺的波特率
          netmask 定義以太網(wǎng)接口的掩碼
          ethaddr 定義以太網(wǎng)接口的MAC地址
          bootfile 定義缺省的下載文件
          bootargs 定義傳遞給Linux內(nèi)核的命令行參數(shù)
          bootcmd 定義自動啟動時執(zhí)行的幾條命令
          serverip 定義tftp服務器端的IP地址
          ipaddr 定義本地的IP地址
          stdin 定義標準輸入設備,一般是串口
          stdout 定義標準輸出設備,一般是串口
          stderr 定義標準出錯信息輸出設備,一般是串口
          4)設備相關:
          標準IO設備數(shù)組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 */
          /* 輸出函數(shù) */
          void (*putc) (const char c); /* To put a char */
          void (*puts) (const char *s); /* To put a string (accelerator) */
          /* 輸入函數(shù) */
          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數(shù)組中。
           在調(diào)用標準IO函數(shù)如printf()時將調(diào)用stdio_devices數(shù)組對應設備的IO函數(shù)如putc()。
          5)命令相關的數(shù)據(jù)結構,后面介紹。
          6)與具體設備有關的數(shù)據(jù)結構,
           如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重定位后的內(nèi)存分布:
             對于smdk2410,RAM范圍從0x30000000~0x34000000. u-boot占用高端內(nèi)存區(qū)。從高地址到低地址內(nèi)存分配如下:


           顯示緩沖區(qū) (.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的重要細節(jié)。

          主要分析流程中各函數(shù)的功能。按啟動順序羅列一下啟動函數(shù)執(zhí)行細節(jié)。按照函數(shù)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。這個宏在所有需要引用全局數(shù)據(jù)指針gd_t *gd的源碼中都有申明。
          這個申明也避免編譯器把r8分配給其它的變量. 所以gd就是r8,這個指針變量不占用內(nèi)存。
          2)gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
          對全局數(shù)據(jù)區(qū)進行地址分配,_armboot_start為0x3f000000,CFG_MALLOC_LEN是堆大小+環(huán)境數(shù)據(jù)區(qū)大小,config/smdk2410.h中CFG_MALLOC_LEN大小定義為192KB.
          3)gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
          分配板子數(shù)據(jù)區(qū)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 */
          不難得出上文所述的內(nèi)存分配結構。
          下面幾個函數(shù)是初始化序列表init_sequence[]中的函數(shù):
          4)cpu_init();定義于cpu/arm920t/cpu.c
          分配IRQ,F(xiàn)IQ棧底地址,由于沒有定義CONFIG_USE_IRQ,所以相當于空實現(xiàn)。
          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;//內(nèi)核啟動參數(shù)存放地址
          6)interrupt_init;定義于cpu/arm920t/s3c24x0/interrupt.c
           初始化2410的PWM timer 4,使其能自動裝載計數(shù)值,恒定的產(chǎn)生時間中斷信號,但是中斷被屏蔽了用不上。
          7)env_init;定義于common/env_flash.c(搜索的時候發(fā)現(xiàn)別的文件也定義了這個函數(shù),而且沒有宏定義保證只有一個被編譯,這是個問題,有高手知道指點一下!)
          功能:指定環(huán)境區(qū)的地址。default_environment是默認的環(huán)境參數(shù)設置。
          gd->env_addr = (ulong)&default_environment[0];
          gd->env_valid = 0;
          8)init_baudrate;初始化全局數(shù)據(jù)區(qū)中波特率的值
          gd->bd->bi_baudrate = gd->baudrate =(i > 0)
          ? (int) simple_strtoul (tmp, NULL, 10)
          : CONFIG_BAUDRATE;
          9)serial_init; 串口通訊設置 定義于cpu/arm920t/s3c24x0/serial.c
           根據(jù)bd中波特率值和pclk,設置串口寄存器。
          10)console_init_f;控制臺前期初始化common/console.c
          由于標準設備還沒有初始化(gd->flags & GD_FLG_DEVINIT=0),這時控制臺使用串口作為控制臺
          函數(shù)只有一句:gd->have_console = 1;
          10)dram_init,初始化內(nèi)存RAM信息。board/smdk2410/smdk2410.c
          其實就是給gd->bd中內(nèi)存信息表賦值而已。
          gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
           gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
           初始化序列表init_sequence[]主要函數(shù)分析結束。
          11)flash_init;定義在board/smdk2410/flash.c
          這個文件與具體平臺關系密切,smdk2410使用的flash與FS2410不一樣,所以移植時這個程序就得重寫。
          flash_init()是必須重寫的函數(shù),它做哪些操作呢?
          首先是有一個變量flash_info_t flash_info[CFG_MAX_FLASH_BANKS]來記錄flash的信息。flash_info_t定義:
          typedef struct {
          ulong size; /* 總大小BYTE */
          ushort sector_count; /* 總的sector數(shù)*/
          ulong flash_id; /* combined device & manufacturer code */
          ulong start[CFG_MAX_FLASH_SECT]; /* 每個sector的起始物理地址。 */
          uchar protect[CFG_MAX_FLASH_SECT]; /* 每個sector的保護狀態(tài),如果置1,在執(zhí)行erase操作的時候?qū)⑻^對應sector*/
          #ifdef CFG_FLASH_CFI //我不管CFI接口。
          .....
          #endif
          } flash_info_t;
          flash_init()的操作就是讀取ID號,ID號指明了生產(chǎn)商和設備號,根據(jù)這些信息設置size,sector_count,flash_id.以及start[]、protect[]。
          12)把視頻幀緩沖區(qū)設置在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區(qū),供malloc使用。下面的變量和函數(shù)定義在lib_arm/board.c
          malloc可用內(nèi)存由mem_malloc_start,mem_malloc_end指定。而當前分配的位置則是mem_malloc_brk。
          mem_malloc_init負責初始化這三個變量。malloc則通過sbrk函數(shù)來使用和管理這片內(nèi)存。
          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() 環(huán)境參數(shù)區(qū)重定位
          由于初始化了heap區(qū),所以可以通過malloc()重新分配一塊環(huán)境參數(shù)區(qū),
          但是沒有必要,因為默認的環(huán)境參數(shù)已經(jīng)重定位到RAM中了。
          /**這里發(fā)現(xiàn)個問題,ENV_IS_EMBEDDED是否有定義還沒搞清楚,而且CFG_MALLOC_LEN也沒有定義,也就是說如果ENV_IS_EMBEDDED沒有定義則執(zhí)行malloc,是不是應該有問題?**/
          15)IP,MAC地址的初始化。主要是從環(huán)境中讀,然后賦給gd->bd對應域就OK。
          16)devices_init ();定義于common/devices.c
          int devices_init (void)//我去掉了編譯選項,注釋掉的是因為對應的編譯選項沒有定義。
          {
          devlist = ListCreate (sizeof (device_t));//創(chuàng)建設備列表
          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 ();
          }
            經(jīng)過devices_init(),創(chuàng)建了devlist,但是只有一個串口設備注冊在內(nèi)。顯然,devlist中的設備都是可以做為console的。

          16) jumptable_init ();初始化gd->jt。1.1.6版本的jumptable只起登記函數(shù)地址的作用。并沒有其他作用。
          17)console_init_r ();后期控制臺初始化
          主要過程:查看環(huán)境參數(shù)stdin,stdout,stderr中對標準IO的指定的設備名稱,再按照環(huán)境指定的名稱搜索devlist,將搜到的設備指針賦給標準IO數(shù)組stdio_devices[]。置gd->flag標志GD_FLG_DEVINIT。這個標志影響putc,getc函數(shù)的實現(xiàn),未定義此標志時直接由串口serial_getc和serial_putc實現(xiàn),定義以后通過標準設備數(shù)組stdio_devices[]中的putc和getc來實現(xiàn)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已經(jīng)初始化
          /* 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[]?

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

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

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


          18)enable_interrupts(),使能中斷。由于CONFIG_USE_IRQ沒有定義,空實現(xiàn)。
             #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)初始化以太網(wǎng)。
          eth_initialize(gd->bd);//bd中已經(jīng)IP,MAC已經(jīng)初始化
          21)main_loop ();定義于common/main.c
          至此所有初始化工作已經(jīng)完畢。main_loop在標準轉(zhuǎn)入設備中接受命令行,然后分析,查找,執(zhí)行。

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

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

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

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

          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); //執(zhí)行這個命令行。

          if (rc <= 0) {
          /* invalid command or not repeatable, forget it */
          lastcommand[0] = 0;
          }
          }
           @run_comman();在命令table中查找匹配的命令名稱,得到對應命令結構體變量指針,以解析得到的參數(shù)調(diào)用其處理函數(shù)執(zhí)行命令。
          @命令結構構體類型定義:command.h中,
          struct cmd_tbl_s {
          char *name; /* 命令名 */
          int maxargs; /* 最大參數(shù)個數(shù)maximum number of arguments */
          int repeatable; /* autorepeat allowed? */
          /* Implementation function 命令執(zhí)行函數(shù)*/
          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實現(xiàn)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。
          其中變量的五個域初始化為括號的內(nèi)容。分別指明了命令名,參數(shù)個數(shù),重復數(shù),執(zhí)行命令的函數(shù),命令提示。
          每個命令都對應這樣一個變量,同時這個結構體變量的section屬性為.u_boot_cmd.也就是說每個變量編譯結束
          在目標文件中都會有一個.u_boot_cmd的section.一個section是連接時的一個輸入段,如.text,.bss,.data等都是section名。
          最后由鏈接程序把所有的.u_boot_cmd段連接在一起,這樣就組成了一個命令結構體數(shù)組。
          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結束的連續(xù)地址范圍內(nèi),
          這樣形成一個cmd_tbl_t類型的數(shù)組,run_command函數(shù)就是在這個數(shù)組中查找命令的。
          3)實現(xiàn)命令處理函數(shù)。命令處理函數(shù)的格式:
          void function (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

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

          然后實現(xiàn)自己的命令處理函數(shù)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的原理圖,從網(wǎng)上得知的結論總結如下,
          fs2410與smdk2410 RAM地址空間大小一致(0x30000000~0x34000000=64MB);

          NOR FLASH型號不一樣,F(xiàn)S2410用SST39VF1601系列的,smdk2410用AMD產(chǎn)LV系列的;

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


          2、移植過程:
          移植u-boot的基本步驟如下
          (1) 在頂層Makefile中為開發(fā)板添加新的配置選項,使用已有的配置項目為例。
          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) 創(chuàng)建一個新目錄存放開發(fā)板相關的代碼,并且添加文件。
          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) 為開發(fā)板添加新的配置文件
          可以先復制參考開發(fā)板的配置文件,再修改。例如:
          $cp include/configs/smdk2410.h include/configs/fs2410.h
          如果是為一顆新的CPU移植,還要創(chuàng)建一個新的目錄存放CPU相關的代碼。

          (4) 配置開發(fā)板
          $ make fs2410_config

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


          5、我實現(xiàn)的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連接實現(xiàn)按字訪問,因此這個地址要左移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(),我實現(xiàn)的比較簡單,因為是與板子嚴重依賴的,只要檢測到的信息與板子提供的已知信息符合就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狀態(tài),讀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生產(chǎn)的FLASH,全部設為0。
          for (i = 0; i < info->sector_count; i++)
          {
          if((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST)
          info->protect[i] = 0;
          }

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

          //設置保護,將u-boot鏡像和環(huán)境參數(shù)所在的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實現(xiàn)
           這里給出修改的部分,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);  //在指定時間內(nèi)不能完成為超時。
          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操作,這個函數(shù)由write_buff一調(diào)用,完成寫入一個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相關的命令中都會間接被調(diào)用。所以u-boot可移植性的另一個方面就是規(guī)定一些函數(shù)調(diào)用接口和全局變量,這些函數(shù)的實現(xiàn)是硬件相關的,移植時只需要實現(xiàn)這些函數(shù)。
          而全局變量是具體硬件無關的。u-boot在通用目錄中實現(xiàn)其余與硬件無關的函數(shù),這些函數(shù)就只與全局變量和函數(shù)接口打交道了。 通過編譯選項設置來靈活控制是否需要編譯通用部分。


          6、增加從Nand 啟動的代碼:
          FS2410板有跳線,跳線短路時從NAND啟動,否則從NOR啟動。根據(jù)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 相關的命令執(zhí)行函數(shù)一開始就判斷這個變量,如果為1立即返回。flash_init()也必須放在這個if(!_boot_flash)條件中。
          這里方法比較笨,主要是為了能在跳線處于任意狀態(tài)時都能啟動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
          //這里已經(jīng)完成搬運到RAM的工作,即將跳轉(zhuǎn)到RAM中_start_armboot函數(shù)中執(zhí)行。
          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數(shù)變量
          ldr r9, _TEXT_BASE //r9指向u-boot在RAM中的起始地址。
          x2:
          ands r0, r8, #0x1f
          bne x3 //此處意思在于頁數(shù)是32的整數(shù)倍的時候才進行一次壞塊檢查 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 //注意這里直接跳轉(zhuǎn)到stack_setup中執(zhí)行
          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狀態(tài),返回值在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內(nèi)部操作完畢
          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()定義(空實現(xiàn))。但是連接時出現(xiàn)問題,原因是u-boot使用的是軟浮點,而我的交叉編譯arm-linux-gcc是硬件浮點。
          看過一些解決方法,比較麻煩,還沒有解決這個問題,希望好心的高手指點。不過我比較納悶,u-boot在nand部分哪里會用到浮點運算呢?

          7、添加網(wǎng)絡命令。
          我嘗試使用ping命令,其余的命令暫時不考慮。
          在common/cmd_net中,首先有條件編譯 #if (CONFIG_COMMANDS & CFG_CMD_NET),然后在命令函數(shù)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。

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

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

           
           
          cortex培訓
          .(2014年7月11)...............................................................................LightTools照明設計培訓課程 AMESIM模擬分析培訓 design expert培訓課程 COMSOL培訓課程 RAMS培訓 齒輪仿真培訓 design expert培訓 material studio培訓 can總線培訓 ZEMAX培訓 CAE分析課程 離心壓縮機培訓課程 KISSsoft培訓課程 Abaqus模擬分析培訓 歐姆龍PLC培訓課程 CAE模擬分析培訓課程 RAMS培訓 齒輪仿真培訓 design expert培訓 Abaqus模擬分析培訓 Aspen Plus Dynamic培訓 光伏逆變器培訓 CAE分析課程 離心壓縮機培訓課程 Python培訓課程 AMESIM模擬分析培訓 COMSOL培訓課程 CAE模擬分析培訓課程 機械應力模擬分析培訓 集成電路培訓 有限元模擬分析培訓 Abaqus模擬分析培訓 Simulink建模仿真培訓 Ansys Workbench模擬培訓 FPGA課程 Labview培訓課程 LightTools照明設計培訓課程 LightTools培訓 歐姆龍PLC培訓課程 CAE模擬分析培訓課程 機械應力模擬分析培訓 集成電路培訓 有限元模擬分析培訓 Abaqus模擬分析培訓 Simulink建模仿真培訓 成都Ansys Workbench模擬培訓 西安FPGA課程 Labview培訓課程 ZEMAX培訓課程 can總線培訓 歐姆龍PLC培訓課程 material studio培訓課程 照明設計培訓 集成電路培訓 汽車總線及SAEJ1939培訓 北京Abaqus模擬分析培訓 蘇州汽車總線培訓 杭州Ansys Workbench模擬培訓 FPGA課程 SAEJ1939培訓課程 數(shù)字集成電路培訓課程 AMESIM模擬分析培訓 廣州歐姆龍PLC培訓課程 CAE模擬分析培訓課程 機械應力模擬分析培訓 集成電路培訓 有限元模擬分析培訓 Abaqus模擬分析培訓 Arduino培訓 Ansys Workbench模擬培訓 FPGA課程 深圳Labview培訓課程 模擬集成電路培訓課程 AMESIM模擬分析培訓 歐姆龍PLC培訓課程 stm32培訓課程 機械應力模擬分析培訓 數(shù)字集成電路后端培訓 有限元模擬分析培訓 Abaqus模擬分析培訓 Simulink建模仿真培訓 Ansys Workbench仿真分析培訓 杭州FPGA課程 杭州Labview培訓課程 集成電路前端設計培訓 AMESIM模擬分析培訓 歐姆龍PLC培訓課程 模擬集成電路前端培訓課程 機械應力模擬分析培訓 集成電路培訓 有限元模擬分析培訓 Abaqus模擬分析培訓 Simulink建模仿真培訓 Ansys Workbench模擬培訓 FPGA課程 Labview培訓課程 R語言醫(yī)藥培訓課程 機器視覺培訓 PLC培訓課程 Vision Pro培訓課程 信號采集分析培訓 集成電路設計培訓 散熱模擬分析培訓 Abaqus模擬培訓 Matlab建模仿真培訓 Ansys Workbench模擬培訓 R語言大數(shù)據(jù)分析培訓課程 工業(yè)機器人仿真培訓 生產(chǎn)線模擬分析培訓課程 運動控制培訓 PLC培訓課程 APD SiP培訓課程 電機控制培訓 集成電路培訓 散熱模擬分析培訓 數(shù)字電源培訓 Matlab數(shù)學建模仿真培訓 Ansys Workbench模擬培訓 R語言培訓課程 數(shù)字逆變器設計培訓 R語言神經(jīng)網(wǎng)絡培訓課程 AMESIM模擬分析培訓 PLC培訓課程 APD SiP培訓課程 工業(yè)設計模擬分析培訓 集成電路培訓 散熱模擬分析培訓 Abaqus熱處理模擬培訓 Matlab建模仿真培訓 Ansys Workbench模擬培訓 R語言大數(shù)據(jù)統(tǒng)計分析培訓課程 AMESIM液壓模擬分析培訓 R語言培訓課程 AMESIM模擬分析培訓 PLC培訓課程 APD SiP培訓課程 模擬分析培訓 集成電路培訓 散熱模擬分析培訓 Abaqus模擬培訓 Matlab建模仿真培訓 Ansys Workbench模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 Matlab培訓課程 Simulink培訓 PLC培訓課程 CAE培訓課程 PDPS模擬分析培訓 ASPEN培訓 ETAP模擬分析培訓 Concepts培訓模擬培訓 Matlab建模培訓 Ansys Workbench散熱培訓 R語言培訓課程 AMESIM模擬分析培訓 數(shù)字集成電路培訓課程 模擬集成電路設計培訓 PLC培訓課程 FPGA培訓課程 模擬電路設計培訓課程 OPENSIM培訓 結構模擬分析培訓 結構疲勞分析培訓模擬培訓 Matlab新能源建模仿真培訓 Ansys Workbench應力分析培訓 R語言培訓課程 AMESIM模擬分析培訓 可靠性分析培訓課程 信號完整性培訓 電路板設計培訓課程 芯片封裝測試培訓課程 FLOEFD模擬分析培訓 光學分析培訓 ZEMAX模擬分析培訓 MAXWELL培訓模擬培訓 Matlab電機設計建模仿真培訓 Ansys Workbench疲勞分析模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 數(shù)字電源設計培訓課程 DSP逆變器設計培訓 DSP電源設計培訓課程 開關電源設計培訓課程 有限元分析培訓 CHEMKIN培訓 SPEOS分析培訓 電機設計培訓 Matlab航空建模仿真培訓 Ansys Workbench傳熱模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 hyperlynx培訓課程 CANOE培訓 PLC培訓課程 CAE培訓課程 PDPS模擬分析培訓 ASPEN培訓 ETAP模擬分析培訓 Concepts培訓模擬培訓 Matlab建模仿真培訓 Ansys Workbench模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 智能物流專用車研發(fā)仿真培訓課程 ANSYS 高級疲勞分析培訓 PLC培訓課程 Geomagic Spark逆向掃描培訓課程 PDPS模擬分析培訓 Simpleware逆向設計培訓 ETAP模擬分析培訓 Fatigue 高級疲勞分析培訓模擬培訓 Matlab建模仿真培訓 Ansys Workbench模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 BIM Bentley STAAD Pro 培訓課程 Pipesim培訓 PLC培訓課程 PipeCalc培訓課程 車燈透鏡光學設計模擬分析培訓 ASPEN培訓 AutoPIPE模擬分析培訓 Neotec Wellflo培訓 Matlab電機控制拖動建模仿真培訓 Ansys Workbench模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 hyperlynx培訓課程 CANOE培訓 PLC培訓課程 CAE培訓課程 PDPS模擬分析培訓 ASPEN培訓 ETAP模擬分析培訓 Concepts培訓模擬培訓 MATLAB、Simulink電力系統(tǒng)建模與仿真培訓 Ansys Workbench模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 高效可再生分布式發(fā)電系統(tǒng)培訓課程 ANSOFT MAXWELL軟件培訓課程 Matlab電機拖動仿真培訓課程 UPS電源培訓課程 電源設計培訓課程 ASPEN培訓 動力電池系統(tǒng)CAE課程培訓課程 大功率開關電源設計技術高級培訓課程 MATLAB航空應用仿真培訓 Ansys Workbench結構應力仿真模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 BMS測試培訓課程 UVC-LED在動態(tài)水處理中的應用培訓 PLC培訓課程 運籌優(yōu)化軟件GAMS應用培訓課程 IsSpice電路模擬分析培訓 熱力熱傳軟件培訓課程 ETAP模擬分析培訓 Concepts培訓模擬培訓 Matlab、Simulink建模仿真培訓 Ansys Workbench結構模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 HYDRUS模型應用培訓課程 CANOE培訓 PLC培訓課程 AMOS培訓課程 PDPS模擬分析培訓 ASPEN培訓 ETAP模擬分析培訓 Concepts培訓模擬培訓 Matlab化學建模仿真培訓 Ansys Workbench結構散熱模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 GMS地下水模擬系統(tǒng)軟件培訓課程 GAMS軟件及CGE模型培訓課程 PLC培訓課程 CAE培訓課程 化學化工仿真軟件培訓課程 ASPEN培訓 ETAP模擬分析培訓 Concepts培訓模擬培訓 Matlab數(shù)學建模仿真培訓 Ansys Workbench疲勞模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 hyperlynx培訓課程 電力仿真系統(tǒng)軟件培訓課程 PLC培訓課程 交通仿真軟件培訓課程 PDPS模擬分析培訓 ASPEN培訓 ETAP模擬分析培訓 Concepts培訓模擬培訓 Matlab流體建模仿真培訓 Ansys Workbench流體模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 NX二次開發(fā)培訓課程 Sigrity培訓 PLC培訓課程 CAE培訓課程 labview模擬分析培訓 ASPEN培訓 ETAP模擬分析培訓 地下水模擬培訓 Matlab機械建模仿真培訓 Ansys Workbench生物模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 電磁兼容培訓課程 電子元器件選型培訓 PLC培訓課程 CAE培訓課程 PDPS模擬分析培訓 ASPEN培訓 ETAP模擬分析培訓 DSPIC模擬培訓 Matlab電磁建模仿真培訓 Ansys Workbench電磁模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 EBSILON培訓課程 SPEOS培訓 Dyrobes培訓課程 ansys培訓課程 NREC模擬分析培訓 ASPEN培訓 齒輪仿真模擬分析培訓 CHEMKIN模擬培訓 Matlab統(tǒng)計建模仿真培訓 Ansys Workbench多相流模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 可靠性培訓課程 MSTOWER培訓 OPENSIM培訓課程 LucidShape培訓課程 Windchill培訓 ASPEN培訓 ETAP模擬分析培訓 DSPIC模擬培訓 Matlab生物建模仿真培訓 Ansys Workbench生物模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 光學培訓課程 PAM CRASH培訓 Dyrobes培訓課程 Fluent培訓課程 數(shù)字電源和逆變器模擬分析培訓 ASPEN培訓 ETAP模擬分析培訓 芯片封裝基板設計模擬培訓 Matlab結構力學建模仿真培訓 Ansys Workbenchb結構力學模擬培訓 R語言培訓課程 AMESIM模擬分析培訓 ZEMAX培訓課程 有限元模擬分析培訓 Altium Designer培訓課程 模擬分析培訓課程 模擬分析培訓 集成電路培訓 散熱模擬分析培訓 R語言培訓 Matlab傳熱建模仿真培訓 Ansys Workbench傳熱模擬分析培訓 R語言培訓課程 AMESIM模擬分析培訓 .......
           
          日韩高清在线高清免费| 国模杨依粉嫩蝴蝶150P| 两个人看的www视频免费完整版| 成人精品免费视频大全app| 中文字幕日韩一区二区三区不| 成人亚洲国产精品久久| 亚洲人成精品久久久久| 最新版天堂资源官网| 乱系列中文字幕在线视频| 日本精品久久久久中文字幕| 久久天天躁夜夜躁狠狠躁2020| 无套进入30p| 丰满人妻被黑人中出849| 婷婷色香五月激情综合2020| 一级成人黄色片| 在线成年人视频| 92国产精品午夜福利| 国产猛男猛女超爽免费视频| 青青草国产免费| 国产乱国产乱老熟300部视频| 超碰97久久国产精品牛牛| 国产免费直播在线观看视频| 综合图区亚洲欧美另类图片| 制服丝袜一区二区三区| 污污网站免费观看| 亚洲欧美国产视频| 日韩精品中文字幕视频一区| 亚洲乱码中文论理电影| 日本边添边摸边做边爱的视频| 久久久久国产一区二区三区| 影音先锋女人aa鲁色资源| 中文字幕电影在线| 好吊日视频在线| 99精品欧美一区二区三区| 国产精品99无码一区二区| 裙子底下真空h揉搓小雪| 国产va免费精品高清在线| 猫咪www免费人成网站| 免费传媒网站免费| 欧美日韩亚洲人人夜夜澡| 亚洲久热无码av中文字幕|