午夜看片网站_国产精品美女久久久久久久久久久_日韩成人_9999777做爰_在线视频 中文字幕_av在线免费播

上海集成電路IC設(shè)計中心

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

 
上海報名熱線:021-51875830
北京報名熱線:010-51292078
深圳報名熱線:4008699035
南京報名熱線:025-68662821
武漢報名熱線:027-50767718
成都報名熱線:4008699035
廣州報名熱線:020-61137349
西安報名熱線:029-86699670
曙海研發(fā)與生產(chǎn)請參見網(wǎng)址:
www.shanghai66.cn
全英文授課課程(Training in English)
  首 頁  手機閱讀模式   課程介紹 企業(yè)培訓(xùn)    培訓(xùn)報名  付款方式   學(xué)員評價 講師介紹   關(guān)于我們   聯(lián)系我們   承接項目  開發(fā)板商城
嵌入式協(xié)處理器--DSP
嵌入式協(xié)處理器--FPGA
FPGA項目實戰(zhàn)系列課程----
嵌入式OS--3G手機操作系統(tǒng)
嵌入式OS-Linux
嵌入式CPU--ARM

嵌入式OS--WinCE
單片機培訓(xùn)
嵌入式硬件設(shè)計
嵌入式OS--VxWorks
友情連接
學(xué)員VIP專區(qū)
用戶名:

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

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

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

 
曙海產(chǎn)品研發(fā)和生產(chǎn)
企業(yè)招聘與人才推薦(免費)

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

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

電子人才網(wǎng)
www.morning-sea.com.cn
授權(quán)機構(gòu)與合作伙伴
現(xiàn)代化的多媒體教室


 

   

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

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

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

#如果是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,生成目標(biāo)文件或者目標(biāo)文件庫。
然后再連接所有目標(biāo)文件(庫)生成最終的u-boot.bin。
連接的主要目標(biāo)(庫)如下:
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)
顯然跟平臺相關(guān)的主要是:
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(見上述)。
其余的均與平臺無關(guān)。
所以考慮移植的時候也主要考慮這幾個目標(biāo)文件(庫)對應(yīng)的目錄。

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

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

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

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

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

二、u-boot的流程、主要的數(shù)據(jù)結(jié)構(gòu)、內(nèi)存分配
1、u-boot的啟動流程:
  從文件層面上看主要流程是在兩個文件中:cpu/arm920t/start.s,lib_arm/board.c, 
  1)start.s 
在flash中執(zhí)行的引導(dǎo)代碼,也就是bootloader中的stage1,負責(zé)初始化硬件環(huán)境,把u-boot從flash加載到RAM中去,然后跳到lib_arm/board.c中的start_armboot中去執(zhí)行。
1.1.6版本的start.s流程:
硬件環(huán)境初始化:
  進入svc模式;關(guān)閉watch dog;屏蔽所有IRQ掩碼;設(shè)置時鐘頻率FCLK、HCLK、PCLK;清I/D cache;禁止MMU和CACHE;配置memory control;
重定位:
  如果當(dāng)前代碼不在連接指定的地址上(對smdk2410是0x3f000000)則需要把u-boot從當(dāng)前位置拷貝到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 (); /* 設(shè)備初始化 */
jumptable_init (); //跳轉(zhuǎn)表初始化
console_init_r (); /* 完整地初始化控制臺設(shè)備 */
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, /* 基本的處理器相關(guān)配置 -- cpu/arm920t/cpu.c */
board_init, /* 基本的板級相關(guān)配置 -- board/smdk2410/smdk2410.c */
interrupt_init, /* 初始化例外處理 -- cpu/arm920t/s3c24x0/interrupt.c */
env_init, /* 初始化環(huán)境變量 -- common/env_flash.c */
init_baudrate, /* 初始化波特率設(shè)置 -- lib_arm/board.c */
serial_init, /* 串口通訊設(shè)置 -- 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ù)結(jié)構(gòu)

u-boot的主要功能是用于引導(dǎo)OS的,但是本身也提供許多強大的功能,可以通過輸入命令行來完成許多操作。所以它本身也是一個很完備的系統(tǒng)。u-boot的大部分操作都是圍繞它自身的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)是通用的,但是不同的板子初始化這些數(shù)據(jù)就不一樣了。所以u-boot的通用代碼是依賴于這些重要的數(shù)據(jù)結(jié)構(gòu)的。這里說的數(shù)據(jù)結(jié)構(gòu)其實就是一些全局變量。
 1)gd 全局數(shù)據(jù)變量指針,它保存了u-boot運行需要的全局數(shù)據(jù),類型定義:
 typedef struct global_data {
bd_t *bd; //board data pointor板子數(shù)據(jù)指針
unsigned long flags;  //指示標(biāo)志,如設(shè)備已經(jīng)初始化標(biāo)志等。
unsigned long baudrate; //串口波特率
unsigned long have_console; /* 串口初始化標(biāo)志*/
unsigned long reloc_off; /* 重定位偏移,就是實際定向的位置與編譯連接時指定的位置之差,一般為0 */
unsigned long env_addr; /* 環(huán)境參數(shù)地址*/
unsigned long env_valid; /* 環(huán)境參數(shù)CRC檢驗有效標(biāo)志 */
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服務(wù)器端的IP地址
ipaddr 定義本地的IP地址
stdin 定義標(biāo)準(zhǔn)輸入設(shè)備,一般是串口
stdout 定義標(biāo)準(zhǔn)輸出設(shè)備,一般是串口
stderr 定義標(biāo)準(zhǔn)出錯信息輸出設(shè)備,一般是串口
4)設(shè)備相關(guān):
標(biāo)準(zhǔn)IO設(shè)備數(shù)組evice_t *stdio_devices[] = { NULL, NULL, NULL };
設(shè)備列表    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把可以用為控制臺輸入輸出的設(shè)備添加到設(shè)備列表devlist,并把當(dāng)前用作標(biāo)準(zhǔn)IO的設(shè)備指針加入stdio_devices數(shù)組中。
 在調(diào)用標(biāo)準(zhǔn)IO函數(shù)如printf()時將調(diào)用stdio_devices數(shù)組對應(yīng)設(shè)備的IO函數(shù)如putc()。
5)命令相關(guān)的數(shù)據(jù)結(jié)構(gòu),后面介紹。
6)與具體設(shè)備有關(guān)的數(shù)據(jù)結(jié)構(gòu),
 如flash_info_t flash_info[CFG_MAX_FLASH_BANKS];記錄nor flash的信息。
 nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; nand flash塊設(shè)備信息
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首地址。
這樣結(jié)合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)存分配結(jié)構(gòu)。
下面幾個函數(shù)是初始化序列表init_sequence[]中的函數(shù):
4)cpu_init();定義于cpu/arm920t/cpu.c
分配IRQ,F(xiàn)IQ棧底地址,由于沒有定義CONFIG_USE_IRQ,所以相當(dāng)于空實現(xiàn)。
5)board_init;極級初始化,定義于board/smdk2410/smdk2410.c
 設(shè)置PLL時鐘,GPIO,使能I/D cache.
設(shè)置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ù)設(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; 串口通訊設(shè)置 定義于cpu/arm920t/s3c24x0/serial.c
 根據(jù)bd中波特率值和pclk,設(shè)置串口寄存器。
10)console_init_f;控制臺前期初始化common/console.c
由于標(biāo)準(zhǔn)設(shè)備還沒有初始化(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ù)分析結(jié)束。
11)flash_init;定義在board/smdk2410/flash.c
這個文件與具體平臺關(guān)系密切,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ū)⑻^對應(yīng)sector*/
#ifdef CFG_FLASH_CFI //我不管CFI接口。
.....
#endif
} flash_info_t;
flash_init()的操作就是讀取ID號,ID號指明了生產(chǎn)商和設(shè)備號,根據(jù)這些信息設(shè)置size,sector_count,flash_id.以及start[]、protect[]。
12)把視頻幀緩沖區(qū)設(shè)置在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);
設(shè)置heap區(qū),供malloc使用。下面的變量和函數(shù)定義在lib_arm/board.c
malloc可用內(nèi)存由mem_malloc_start,mem_malloc_end指定。而當(dāng)前分配的位置則是mem_malloc_brk。
mem_malloc_init負責(zé)初始化這三個變量。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,是不是應(yīng)該有問題?**/
15)IP,MAC地址的初始化。主要是從環(huán)境中讀,然后賦給gd->bd對應(yīng)域就OK。
16)devices_init ();定義于common/devices.c
int devices_init (void)//我去掉了編譯選項,注釋掉的是因為對應(yīng)的編譯選項沒有定義。
{
devlist = ListCreate (sizeof (device_t));//創(chuàng)建設(shè)備列表
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 ();  //這里其實是定義了一個串口設(shè)備,并且注冊到devlist中。
//serial_devices_init ();
//drv_usbtty_init ();
//drv_nc_init ();
}
  經(jīng)過devices_init(),創(chuàng)建了devlist,但是只有一個串口設(shè)備注冊在內(nèi)。顯然,devlist中的設(shè)備都是可以做為console的。

16) jumptable_init ();初始化gd->jt。1.1.6版本的jumptable只起登記函數(shù)地址的作用。并沒有其他作用。
17)console_init_r ();后期控制臺初始化
主要過程:查看環(huán)境參數(shù)stdin,stdout,stderr中對標(biāo)準(zhǔn)IO的指定的設(shè)備名稱,再按照環(huán)境指定的名稱搜索devlist,將搜到的設(shè)備指針賦給標(biāo)準(zhǔn)IO數(shù)組stdio_devices[]。置gd->flag標(biāo)志GD_FLG_DEVINIT。這個標(biāo)志影響putc,getc函數(shù)的實現(xiàn),未定義此標(biāo)志時直接由串口serial_getc和serial_putc實現(xiàn),定義以后通過標(biāo)準(zhǔn)設(shè)備數(shù)組stdio_devices[]中的putc和getc來實現(xiàn)IO。
下面是相關(guān)代碼:
void putc (const char c)
{
#ifdef CONFIG_SILENT_CONSOLE
if (gd->flags & GD_FLG_SILENT)//GD_FLG_SILENT無輸出標(biāo)志
return;
#endif
if (gd->flags & GD_FLG_DEVINIT) {//設(shè)備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)標(biāo)準(zhǔn)IO重定向,任何可以作為標(biāo)準(zhǔn)IO的設(shè)備,如USB鍵盤,LCD屏,串口等都可以對應(yīng)一個device_t的結(jié)構(gòu)體變量,只需要實現(xiàn)getc和putc等函數(shù),就能加入到devlist列表中去,也就可以被assign為標(biāo)準(zhǔn)IO設(shè)備std_device中去。如函數(shù)

int console_assign (int file, char *devname); /* Assign the console 重定向標(biāo)準(zhǔn)輸入輸出*/

這個函數(shù)功能就是把名為devname的設(shè)備重定向為標(biāo)準(zhǔn)IO文件file(stdin,stdout,stderr)。其執(zhí)行過程是在devlist中查找devname的設(shè)備,返回這個設(shè)備的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)設(shè)置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在標(biāo)準(zhǔn)轉(zhuǎn)入設(shè)備中接受命令行,然后分析,查找,執(zhí)行。

關(guān)于U-boot中命令相關(guān)的編程:

1、命令相關(guān)的函數(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)境中設(shè)置的啟動命令行
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中查找匹配的命令名稱,得到對應(yīng)命令結(jié)構(gòu)體變量指針,以解析得到的參數(shù)調(diào)用其處理函數(shù)執(zhí)行命令。
@命令結(jié)構(gòu)構(gòu)體類型定義: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屬性的結(jié)構(gòu)體。編譯的時候會單獨生成一個名為.u_boot_cmd的section段。
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))


//這個宏定義一個命令結(jié)構(gòu)體變量。并用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_* 命令選項位標(biāo)志。在include/cmd_confdefs.h 中定義。
每個板子的配置文件(如include/config/smdk2410.h)中都可以定義u-boot
需要的命令,如果要添加一個命令,必須添加相應(yīng)的命令選項。如下:
#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文件夾下對應(yīng)一個源文件
cmd_*.c ,如cmd_cache.c實現(xiàn)cache命令。 文件開頭就有一行編譯條件:
#if(CONFIG_COMMANDS&CFG_CMD_CACHE)
也就是說,如果配置頭文件中CONFIG_COMMANDS不或上相應(yīng)命令的選項,這里就不會被編譯。
 2)定義命令結(jié)構(gòu)體變量,如:
  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類型的結(jié)構(gòu)體變量,這個結(jié)構(gòu)體變量名為__u_boot_cmd_dcache。
其中變量的五個域初始化為括號的內(nèi)容。分別指明了命令名,參數(shù)個數(shù),重復(fù)數(shù),執(zhí)行命令的函數(shù),命令提示。
每個命令都對應(yīng)這樣一個變量,同時這個結(jié)構(gòu)體變量的section屬性為.u_boot_cmd.也就是說每個變量編譯結(jié)束
在目標(biāo)文件中都會有一個.u_boot_cmd的section.一個section是連接時的一個輸入段,如.text,.bss,.data等都是section名。
最后由鏈接程序把所有的.u_boot_cmd段連接在一起,這樣就組成了一個命令結(jié)構(gòu)體數(shù)組。
u-boot.lds中相應(yīng)腳本如下:
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
可以看到所有的命令結(jié)構(gòu)體變量集中在__u_boot_cmd_start開始到__u_boot_cmd_end結(jié)束的連續(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)自己的命令,應(yīng)該在include/com_confdefs.h中定義一個命令選項標(biāo)志位。
在板子的配置文件中添加命令自己的選項。按照u-boot的風(fēng)格,可以在common/下面添加自己的cmd_*.c,并且定義自己的命令結(jié)構(gòu)體變量,如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)上得知的結(jié)論總結(jié)如下,
fs2410與smdk2410 RAM地址空間大小一致(0x30000000~0x34000000=64MB);

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

網(wǎng)絡(luò)芯片型號和在內(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ā)板相關(guān)的代碼,并且添加文件。
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ù)制參考開發(fā)板的配置文件,再修改。例如:
$cp include/configs/smdk2410.h include/configs/fs2410.h
如果是為一顆新的CPU移植,還要創(chuàng)建一個新的目錄存放CPU相關(guān)的代碼。

(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板子相關(guān)的nor flash函數(shù)操作。由于write_buffer中調(diào)用了write_hword去具體寫入一個字到flash中,這個函數(shù)本身是與硬件無關(guān)的,
所以與硬件密切相關(guān)的三個需要重寫的函數(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結(jié)構(gòu)體變量中的sector_count是block數(shù),起始地址表保存也是所有block的起始地址。
SST Manufacturer ID = 00BFH ;
SST39VF1601 Device ID = 234BH;
軟件命令序列如下圖。


5、我實現(xiàn)的flash.c主要部分:

//相關(guān)定義:
# 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);
}

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

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

//設(shè)置保護,將u-boot鏡像和環(huán)境參數(shù)所在的block的proctect標(biāo)志置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相關(guān)的命令中都會間接被調(diào)用。所以u-boot可移植性的另一個方面就是規(guī)定一些函數(shù)調(diào)用接口和全局變量,這些函數(shù)的實現(xiàn)是硬件相關(guān)的,移植時只需要實現(xiàn)這些函數(shù)。
而全局變量是具體硬件無關(guān)的。u-boot在通用目錄中實現(xiàn)其余與硬件無關(guān)的函數(shù),這些函數(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的初始化代碼和與它相關(guān)的命令都是不能使用的。
這里我采用比較簡單的方法,定義一個全局變量標(biāo)志_boot_flash保存當(dāng)前啟動FLASH標(biāo)志,_boot_flash=0則表明是NOR啟動,否則是從NAND。
在每個與nor flash 相關(guān)的命令執(zhí)行函數(shù)一開始就判斷這個變量,如果為1立即返回。flash_init()也必須放在這個if(!_boot_flash)條件中。
這里方法比較笨,主要是為了能在跳線處于任意狀態(tài)時都能啟動u-boot。
修改后的start.s如下。
.......
//修改1
.globl _boot_flash
_boot_flash: //定義全局標(biāo)志變量,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中設(shè)置當(dāng)前啟動flash設(shè)備是NOR還是NAND
//這里已經(jīng)完成搬運到RAM的工作,即將跳轉(zhuǎn)到RAM中_start_armboot函數(shù)中執(zhí)行。
adr r1,_boot_flash //取_boot_flash的當(dāng)前地址,這時還在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] //如果當(dāng)前是從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表明當(dāng)前塊不是壞塊。
cmp r0, #0
addne r8, r8, #32 //如果當(dāng)前塊壞了,跳過讀取操作。 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

關(guān)于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)絡(luò)命令。
我嘗試使用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)
并且設(shè)定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防火墻的問題。關(guān)閉防火墻就能PING通了。

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

 
 
版權(quán)所有:曙海信息網(wǎng)絡(luò)科技有限公司 copyright 2000-2016
 
上海總部培訓(xùn)基地

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

地址:北京市昌平區(qū)沙河南街11號312室
(地鐵昌平線沙河站B出口) 郵編:102200 行走路線:請點擊這查看
熱線:010-51292078
傳真:010-51292078
業(yè)務(wù)手機:15701686205
E-mail:qianru@51qianru.cn
客服QQ:1243285887
深圳培訓(xùn)基地

地址:深圳市環(huán)觀中路28號82#201室

熱線:4008699035
傳真:4008699035
業(yè)務(wù)手機:13699831341

郵編:518001
信箱:qianru2@51qianru.cn
客服QQ:2472106501
南京培訓(xùn)基地

地址:江蘇省南京市棲霞區(qū)和燕路251號金港大廈B座2201室
(地鐵一號線邁皋橋站1號出口旁,近南京火車站)
熱線:025-68662821
傳真:025-68662821
郵編:210046
信箱:qianru3@51qianru.cn
客服QQ:1325341129
 
成都培訓(xùn)基地

地址:四川省成都市高新區(qū)中和大道一段99號領(lǐng)館區(qū)1號1-3-2903 郵編:610031
熱線:4008699035 業(yè)務(wù)手機:13540421960
客服QQ:1325341129 E-mail:qianru4@51qianru.cn
武漢培訓(xùn)基地

地址:湖北省武漢市江岸區(qū)漢江北路34號 九運大廈401室 郵編:430022
熱線:4008699035
客服QQ:849322415
E-mail:qianru5@51qianru.cn
廣州培訓(xùn)基地

地址:廣州市越秀區(qū)環(huán)市東路486號廣糧大廈1202室

熱線:020-61137349
傳真:020-61137349

郵編:510075
信箱:qianru6@51qianru.cn
西安培訓(xùn)基地

地址:西安市高新區(qū)城南電子西街2號融僑紫薇2#402室

熱線:029-86699670
業(yè)務(wù)手機:18392016509
傳真:029-86699670
郵編:710054
信箱:qianru7@51qianru.cn
 
沈陽培訓(xùn)基地

地址:遼寧省沈陽市東陵渾南新區(qū)沈營路六宅臻品29-11-9 郵編:110179
熱線:4008699035
E-mail:qianru8@51qianru.cn
鄭州培訓(xùn)基地

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

熱線:4008699035

郵編:450001
信箱:qianru9@51qianru.cn
石家莊培訓(xùn)基地

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

熱線:4008699035
業(yè)務(wù)手機:13933071028
傳真:4008699035
郵編:050200
信箱:qianru10@51qianru.cn
 

雙休日、節(jié)假日及晚上可致電值班電話:021-51875830 值班手機:15921673576


備案號:滬ICP備08026168號

.(2014年7月11).............................................................................................R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) PLC培訓(xùn)課程 APD SiP培訓(xùn)課程 模擬分析培訓(xùn) 集成電路培訓(xùn) 散熱模擬分析培訓(xùn) Abaqus模擬培訓(xùn) Matlab建模仿真培訓(xùn) Ansys Workbench模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) PLC培訓(xùn)課程 APD SiP培訓(xùn)課程 模擬分析培訓(xùn) 集成電路培訓(xùn) 散熱模擬分析培訓(xùn) Abaqus模擬培訓(xùn) Matlab建模仿真培訓(xùn) Ansys Workbench模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) PLC培訓(xùn)課程 APD SiP培訓(xùn)課程 模擬分析培訓(xùn) 集成電路培訓(xùn) 散熱模擬分析培訓(xùn) Abaqus模擬培訓(xùn) Matlab建模仿真培訓(xùn) Ansys Workbench模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) Matlab培訓(xùn)課程 Simulink培訓(xùn) PLC培訓(xùn)課程 CAE培訓(xùn)課程 PDPS模擬分析培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) Concepts培訓(xùn)模擬培訓(xùn) Matlab建模培訓(xùn) Ansys Workbench散熱培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) 數(shù)字集成電路培訓(xùn)課程 模擬集成電路設(shè)計培訓(xùn) PLC培訓(xùn)課程 FPGA培訓(xùn)課程 模擬電路設(shè)計培訓(xùn)課程 OPENSIM培訓(xùn) 結(jié)構(gòu)模擬分析培訓(xùn) 結(jié)構(gòu)疲勞分析培訓(xùn)模擬培訓(xùn) Matlab新能源建模仿真培訓(xùn) Ansys Workbench應(yīng)力分析培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) 可靠性分析培訓(xùn)課程 信號完整性培訓(xùn) 電路板設(shè)計培訓(xùn)課程 芯片封裝測試培訓(xùn)課程 FLOEFD模擬分析培訓(xùn) 光學(xué)分析培訓(xùn) ZEMAX模擬分析培訓(xùn) MAXWELL培訓(xùn)模擬培訓(xùn) Matlab電機設(shè)計建模仿真培訓(xùn) Ansys Workbench疲勞分析模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) 數(shù)字電源設(shè)計培訓(xùn)課程 DSP逆變器設(shè)計培訓(xùn) DSP電源設(shè)計培訓(xùn)課程 開關(guān)電源設(shè)計培訓(xùn)課程 有限元分析培訓(xùn) CHEMKIN培訓(xùn) SPEOS分析培訓(xùn) 電機設(shè)計培訓(xùn) Matlab航空建模仿真培訓(xùn) Ansys Workbench傳熱模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) hyperlynx培訓(xùn)課程 CANOE培訓(xùn) PLC培訓(xùn)課程 CAE培訓(xùn)課程 PDPS模擬分析培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) Concepts培訓(xùn)模擬培訓(xùn) Matlab建模仿真培訓(xùn) Ansys Workbench模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) 智能物流專用車研發(fā)仿真培訓(xùn)課程 ANSYS 高級疲勞分析培訓(xùn) PLC培訓(xùn)課程 Geomagic Spark逆向掃描培訓(xùn)課程 PDPS模擬分析培訓(xùn) Simpleware逆向設(shè)計培訓(xùn) ETAP模擬分析培訓(xùn) Fatigue 高級疲勞分析培訓(xùn)模擬培訓(xùn) Matlab建模仿真培訓(xùn) Ansys Workbench模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) BIM Bentley STAAD Pro 培訓(xùn)課程 Pipesim培訓(xùn) PLC培訓(xùn)課程 PipeCalc培訓(xùn)課程 車燈透鏡光學(xué)設(shè)計模擬分析培訓(xùn) ASPEN培訓(xùn) AutoPIPE模擬分析培訓(xùn) Neotec Wellflo培訓(xùn) Matlab電機控制拖動建模仿真培訓(xùn) Ansys Workbench模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) hyperlynx培訓(xùn)課程 CANOE培訓(xùn) PLC培訓(xùn)課程 CAE培訓(xùn)課程 PDPS模擬分析培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) Concepts培訓(xùn)模擬培訓(xùn) MATLAB、Simulink電力系統(tǒng)建模與仿真培訓(xùn) Ansys Workbench模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) 高效可再生分布式發(fā)電系統(tǒng)培訓(xùn)課程 ANSOFT MAXWELL軟件培訓(xùn)課程 Matlab電機拖動仿真培訓(xùn)課程 UPS電源培訓(xùn)課程 電源設(shè)計培訓(xùn)課程 ASPEN培訓(xùn) 動力電池系統(tǒng)CAE課程培訓(xùn)課程 大功率開關(guān)電源設(shè)計技術(shù)高級培訓(xùn)課程 MATLAB航空應(yīng)用仿真培訓(xùn) Ansys Workbench結(jié)構(gòu)應(yīng)力仿真模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) BMS測試培訓(xùn)課程 UVC-LED在動態(tài)水處理中的應(yīng)用培訓(xùn) PLC培訓(xùn)課程 運籌優(yōu)化軟件GAMS應(yīng)用培訓(xùn)課程 IsSpice電路模擬分析培訓(xùn) 熱力熱傳軟件培訓(xùn)課程 ETAP模擬分析培訓(xùn) Concepts培訓(xùn)模擬培訓(xùn) Matlab、Simulink建模仿真培訓(xùn) Ansys Workbench結(jié)構(gòu)模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) HYDRUS模型應(yīng)用培訓(xùn)課程 CANOE培訓(xùn) PLC培訓(xùn)課程 AMOS培訓(xùn)課程 PDPS模擬分析培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) Concepts培訓(xùn)模擬培訓(xùn) Matlab化學(xué)建模仿真培訓(xùn) Ansys Workbench結(jié)構(gòu)散熱模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) GMS地下水模擬系統(tǒng)軟件培訓(xùn)課程 GAMS軟件及CGE模型培訓(xùn)課程 PLC培訓(xùn)課程 CAE培訓(xùn)課程 化學(xué)化工仿真軟件培訓(xùn)課程 ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) Concepts培訓(xùn)模擬培訓(xùn) Matlab數(shù)學(xué)建模仿真培訓(xùn) Ansys Workbench疲勞模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) hyperlynx培訓(xùn)課程 電力仿真系統(tǒng)軟件培訓(xùn)課程 PLC培訓(xùn)課程 交通仿真軟件培訓(xùn)課程 PDPS模擬分析培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) Concepts培訓(xùn)模擬培訓(xùn) Matlab流體建模仿真培訓(xùn) Ansys Workbench流體模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) NX二次開發(fā)培訓(xùn)課程 Sigrity培訓(xùn) PLC培訓(xùn)課程 CAE培訓(xùn)課程 labview模擬分析培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) 地下水模擬培訓(xùn) Matlab機械建模仿真培訓(xùn) Ansys Workbench生物模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) 電磁兼容培訓(xùn)課程 電子元器件選型培訓(xùn) PLC培訓(xùn)課程 CAE培訓(xùn)課程 PDPS模擬分析培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) DSPIC模擬培訓(xùn) Matlab電磁建模仿真培訓(xùn) Ansys Workbench電磁模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) EBSILON培訓(xùn)課程 SPEOS培訓(xùn) Dyrobes培訓(xùn)課程 ansys培訓(xùn)課程 NREC模擬分析培訓(xùn) ASPEN培訓(xùn) 齒輪仿真模擬分析培訓(xùn) CHEMKIN模擬培訓(xùn) Matlab統(tǒng)計建模仿真培訓(xùn) Ansys Workbench多相流模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) 可靠性培訓(xùn)課程 MSTOWER培訓(xùn) OPENSIM培訓(xùn)課程 LucidShape培訓(xùn)課程 Windchill培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) DSPIC模擬培訓(xùn) Matlab生物建模仿真培訓(xùn) Ansys Workbench生物模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) 光學(xué)培訓(xùn)課程 PAM CRASH培訓(xùn) Dyrobes培訓(xùn)課程 Fluent培訓(xùn)課程 數(shù)字電源和逆變器模擬分析培訓(xùn) ASPEN培訓(xùn) ETAP模擬分析培訓(xùn) 芯片封裝基板設(shè)計模擬培訓(xùn) Matlab結(jié)構(gòu)力學(xué)建模仿真培訓(xùn) Ansys Workbenchb結(jié)構(gòu)力學(xué)模擬培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) ZEMAX培訓(xùn)課程 有限元模擬分析培訓(xùn) Altium Designer培訓(xùn)課程 模擬分析培訓(xùn)課程 模擬分析培訓(xùn) 集成電路培訓(xùn) 散熱模擬分析培訓(xùn) R語言培訓(xùn) Matlab傳熱建模仿真培訓(xùn) Ansys Workbench傳熱模擬分析培訓(xùn) R語言培訓(xùn)課程 AMESIM模擬分析培訓(xùn) .....
在線客服
主站蜘蛛池模板: 久草热久草热线频97精品 | 国产日本视频 | 久久99网站 | 妖精视频黄 | 毛片在线全部免费观看 | 亚洲精品无码成人 | 四虎成人www国产精品 | 色播久久人人爽人人爽人人片av | 国产偷国产偷亚洲高清在线 | 精品亚洲视频在线观看 | 亚洲自拍小视频 | 色综合天天综合网国产成人网 | 99久久综合国产精品免费 | 日韩精品无码久久一区二区三 | 久久精品国产精品亚洲20 | 麻豆 一区 精品 在线 | 日韩欧美视频在线一区二区 | 亚洲av永久无码精品无码流畅 | 精品国产一区二区三区四区在线看 | 少妇裸体性生交 | 国产精品视频在线免费观看 | 国产精品偷伦视频免费观看的 | 色翁荡息又大又硬又粗又爽 | 伊人久久香蕉 | 欧美高清一区二区三 | 亚洲男人的天堂网站 | 亚洲男人天堂a | 2019年中文字字幕在线看不卡 | 日本毛片视频 | 亚洲日韩精品无码专区网址 | 国产免费青青青免费视频观看 | 99久久精品日本一区二区免费 | 亚洲av乱码久久精品蜜桃 | 国产特黄特色a级在线视 | 久久综合狠狠综合久久综合88 | 2022国产成人综合精品 | 久久电影精品久久99久久 | 青青草国产精品久久 | 成人中文字幕一区二区三区 | 美女被免费视频网站a | 最新亚洲精品国偷自产在线 |