u-boot.bin
这里的u-boot.bin指的是不包含SPL的stage2部分的代码. 它会被SPL搬移到RAM的某个地址处开始运行. 本篇下面提到的u-boot.bin时, 也是指的这个概念.
u-boot.bin的文件组成
当我们在uboot下执行make命令的时候, 它最核心的功能是执行Makefile中的all目标编译出相应的文件. 我们来看看这个all目标
[plain] view plain copy
- all: $(ALL-y) $(SUBDIR_EXAMPLES)
all依赖于$(ALL-y) 和 $(SUBDIR_EXAMPLES), 这里我只关注ALL-y, 如下:
[plain] view plain copy
- # Always append ALL so that arch config.mk's can add custom ones
- ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map</span>
- ALL-$(CONFIG_NAND_U_BOOT) += $(obj)u-boot-nand.bin
- ALL-$(CONFIG_ONENAND_U_BOOT) += $(obj)u-boot-onenand.bin
- ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin
- ALL-$(CONFIG_SPL_FRAMEWORK) += $(obj)u-boot.img
- ALL-$(CONFIG_TPL) += $(obj)tpl/u-boot-tpl.bin
- ALL-$(CONFIG_OF_SEPARATE) += $(obj)u-boot.dtb $(obj)u-boot-dtb.bin
- ifneq ($(CONFIG_SPL_TARGET),)
- ALL-$(CONFIG_SPL) += $(obj)$(subst ",,$(CONFIG_SPL_TARGET))
- endif
- # enable combined SPL/u-boot/dtb rules for tegra
- ifneq ($(CONFIG_TEGRA),)
- ifeq ($(CONFIG_OF_SEPARATE),y)
- ALL-y += $(obj)u-boot-dtb-tegra.bin
- else
- ALL-y += $(obj)u-boot-nodtb-tegra.bin
- endif
- endif
注意红色部分的代码, 它表面all目标依赖 $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map
先忽略System.map, 在看下面一段
[plain] view plain copy
- $(obj)u-boot.srec: $(obj)u-boot
- $(OBJCOPY) -O srec $< $@
- $(obj)u-boot.bin: $(obj)u-boot
- $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
- $(BOARD_SIZE_CHECK)
它们都依赖于u-boot
那么在看看u-boot的依赖关系
[plain] view plain copy
- ifeq ($(CONFIG_SANDBOX),y)
- GEN_UBOOT = \
- cd $(LNDIR) && $(CC) $(SYMS) -T $(obj)u-boot.lds \
- -Wl,–start-group $(__LIBS) -Wl,–end-group \
- $(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map -o u-boot
- else
- GEN_UBOOT = \
- cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) \
- $(__OBJS) \
- –start-group $(__LIBS) –end-group $(PLATFORM_LIBS) \
- -Map u-boot.map -o u-boot
- endif
- $(obj)u-boot: depend \
- $(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
- $(GEN_UBOOT)
- depend: 参考附录中的depend
- $(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) : 各个子目录
- $(LDSCRIPT) : 如下, 默认就是在arch/arm/cpu/下面的u-boot.lds
-
[plain] view plain copy
- # If board code explicitly specified LDSCRIPT or CONFIG_SYS_LDSCRIPT, use
- # that (or fail if absent). Otherwise, search for a linker script in a
- # standard location.
- LDSCRIPT_MAKEFILE_DIR = $(dir $(LDSCRIPT))
- ifndef LDSCRIPT
- #LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debug
- ifdef CONFIG_SYS_LDSCRIPT
- # need to strip off double quotes
- LDSCRIPT := $(subst ",,$(CONFIG_SYS_LDSCRIPT))
- endif
- endif
- # If there is no specified link script, we look in a number of places for it
- ifndef LDSCRIPT
- ifeq ($(CONFIG_NAND_U_BOOT),y)
- LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
- ifeq ($(wildcard $(LDSCRIPT)),)
- LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
- endif
- endif
- ifeq ($(wildcard $(LDSCRIPT)),)
- LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
- endif
- ifeq ($(wildcard $(LDSCRIPT)),)
- LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot.lds
- endif
- ifeq ($(wildcard $(LDSCRIPT)),)
- LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot.lds
- # We don't expect a Makefile here
- LDSCRIPT_MAKEFILE_DIR =
- endif
- ifeq ($(wildcard $(LDSCRIPT)),)
- $(error could not find linker script)
- endif
- endif
- $(GEN_UBOOT) : 利用ld命令链接生成u-boot
OK, 这就是u-boot.bin相关的文件组成. 如果想知道到底编译了哪些目录, 可以根据Makefile仔细研究它到底依赖了哪些文件夹. 原理与上面类似.
代码分析
u-boot.lds: 它的位置在上文中我们分析了
- 根据u-boot.lds中的规则, 与SPL阶段一样, CPUDIR/start.o被放在了最前面. 它与SPL阶段对应的是同一个文件arch/arm/cpu/armv7/start.S
start.S
与SPL中start.S的执行流程基本一致, 不同的地方是, 这里的start.S会负责处理异常中断.
ctr0.S
_main
[plain] view plain copy
- ENTRY(_main)
- /*
- * Set up initial C runtime environment and call board_init_f(0).
- */
- #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
- ldr sp, =(CONFIG_SPL_STACK)
- #else
- ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
- #endif
- bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
- sub sp, #GD_SIZE /* allocate one GD above SP */
- bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
- mov r9, sp /* GD is above SP */
- mov r0, #0
- bl board_init_f
- sp, gd相关, 参考SPL中的_main
- bl board_init_f: 跳转到board_init_f
- SPL最后也是跳转到board_init_f, 它们有什么不一样吗? 是的, 完全不一样. 由于SPL阶段与u-boot.bin阶段编译选项的不同, 会导致不同的c文件被编译. 这里的board_init_f实现函数与SPL阶段board_init_f的实现函数不是同一个.
[plain] view plain copy
- #if ! defined(CONFIG_SPL_BUILD)
- /*
- * Set up intermediate environment (new sp and gd) and call
- * relocate_code(addr_moni). Trick here is that we'll return
- * 'here' but relocated.
- */
- ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
- bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
- ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
- sub r9, r9, #GD_SIZE /* new GD is below bd */
- adr lr, here
- ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
- add lr, lr, r0
- ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
- b relocate_code
- here:
- /* Set up final (full) environment */
- bl c_runtime_cpu_setup /* we still call old routine here */
- ldr r0, =__bss_start /* this is auto-relocated! */
- ldr r1, =__bss_end /* this is auto-relocated! */
- mov r2, #0x00000000 /* prepare zero to clear BSS */
- clbss_l:cmp r0, r1 /* while not at end of BSS */
- strlo r2, [r0] /* clear 32-bit BSS word */
- addlo r0, r0, #4 /* move to next */
- blo clbss_l
- bl coloured_LED_init
- bl red_led_on
- /* call board_init_r(gd_t *id, ulong dest_addr) */
- mov r0, r9 /* gd_t */
- ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
- /* call board_init_r */
- ldr pc, =board_init_r /* this is auto-relocated! */
- /* we should not return here. */
- #endif
- #if ! defined(CONFIG_SPL_BUILD) : 说明只有在u-boot.bin阶段, 才会执行这段代码. 这里也与SPL阶段不一样
- b relocate_code : 把代码relocate到编译地址处
- ldr pc, =board_init_r: 调用board_init_r, 同样, 该函数实现的地方与SPL阶段的也不一样.
arch/arm/lib/board.c
board_init_f
[plain] view plain copy
- memset((void *)gd, 0, sizeof(gd_t));
- gd的定义在board.c里面, 有一行: DECLARE_GLOBAL_DATA_PTR;
- memset清零, 说明从这里开始, 重新对gd进行初始化.
- 那我们在前面的代码很看到很多对gd的操作啊, 有什么用呢? 主要是在一些汇编代码里面引用了gd的相关变量. 哪些变量可能被引用到呢? 在这里 lib/asm-offsets.c
[plain] view plain copy
- #ifdef CONFIG_OF_EMBED
- /* Get a pointer to the FDT */
- gd->fdt_blob = _binary_dt_dtb_start;
- #elif defined CONFIG_OF_SEPARATE
- /* FDT is at end of image */
- gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
- #endif
- /* Allow the early environment to override the fdt address */
- gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
- (uintptr_t)gd->fdt_blob);
- fdt相关, 类似于linux内核里面的dtb. 暂不分析
[plain] view plain copy
- for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
- if ((*init_fnc_ptr)() != 0) {
- hang ();
- }
- }
- 依次调用init_sequence里面的函数, 如果某个函数执行失败, 即返回值不为0, 就会hang
- 具体有哪些函数, 见下文.
[plain] view plain copy
- #ifdef CONFIG_OF_CONTROL
- /* For now, put this check after the console is ready */
- if (fdtdec_prepare_fdt()) {
- panic("** CONFIG_OF_CONTROL defined but no FDT – please see "
- "doc/README.fdt-control");
- }
- #endif
- fdt相关, 暂不分析
[plain] view plain copy
- #if defined(CONFIG_SYS_MEM_TOP_HIDE)
- /*
- * Subtract specified amount of memory to hide so that it won't
- * get "touched" at all by U-Boot. By fixing up gd->ram_size
- * the Linux kernel should now get passed the now "corrected"
- * memory size and won't touch it either. This should work
- * for arch/ppc and arch/powerpc. Only Linux board ports in
- * arch/powerpc with bootwrapper support, that recalculate the
- * memory size from the SDRAM controller setup will have to
- * get fixed.
- */
- gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
- #endif
- 隐藏一块MEM, uboot不会touch它. 同样也可以做到让linux kernel不touch它.
- 一般ppc或者powerpc体系架构才会用到这个
[plain] view plain copy
- addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
- #ifdef CONFIG_LOGBUFFER
- #ifndef CONFIG_ALT_LB_ADDR
- /* reserve kernel log buffer */
- addr -= (LOGBUFF_RESERVE);
- debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
- addr);
- #endif
- #endif
- #ifdef CONFIG_PRAM
- /*
- * reserve protected RAM
- */
- reg = getenv_ulong("pram", 10, CONFIG_PRAM);
- addr -= (reg << 10); /* size is in kB */
- debug("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
- #endif /* CONFIG_PRAM */
- #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
- /* reserve TLB table */
- gd->arch.tlb_size = 4096 * 4;
- addr -= gd->arch.tlb_size;
- /* round down to next 64 kB limit */
- addr &= ~(0x10000 – 1);
- gd->arch.tlb_addr = addr;
- debug("TLB table from %08lx to %08lx\n", addr, addr + gd->arch.tlb_size);
- #endif
- /* round down to next 4 kB limit */
- addr &= ~(4096 – 1);
- debug("Top of RAM usable for U-Boot at: %08lx\n", addr);
- #ifdef CONFIG_LCD
- #ifdef CONFIG_FB_ADDR
- gd->fb_base = CONFIG_FB_ADDR;
- #else
- /* reserve memory for LCD display (always full pages) */
- addr = lcd_setmem(addr);
- gd->fb_base = addr;
- #endif /* CONFIG_FB_ADDR */
- #endif /* CONFIG_LCD */
- /*
- * reserve memory for U-Boot code, data & bss
- * round down to next 4 kB limit
- */
- addr -= gd->mon_len;
- addr &= ~(4096 – 1);
- debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
- #ifndef CONFIG_SPL_BUILD
- /*
- * reserve memory for malloc() arena
- */
- addr_sp = addr – TOTAL_MALLOC_LEN;
- debug("Reserving %dk for malloc() at: %08lx\n",
- TOTAL_MALLOC_LEN >> 10, addr_sp);
- /*
- * (permanently) allocate a Board Info struct
- * and a permanent copy of the "global" data
- */
- addr_sp -= sizeof (bd_t);
- bd = (bd_t *) addr_sp;
- gd->bd = bd;
- debug("Reserving %zu Bytes for Board Info at: %08lx\n",
- sizeof (bd_t), addr_sp);
- #ifdef CONFIG_MACH_TYPE
- gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
- #endif
- addr_sp -= sizeof (gd_t);
- id = (gd_t *) addr_sp;
- debug("Reserving %zu Bytes for Global Data at: %08lx\n",
- sizeof (gd_t), addr_sp);
- #if defined(CONFIG_OF_SEPARATE) && defined(CONFIG_OF_CONTROL)
- /*
- * If the device tree is sitting immediate above our image then we
- * must relocate it. If it is embedded in the data section, then it
- * will be relocated with other data.
- */
- if (gd->fdt_blob) {
- fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
- addr_sp -= fdt_size;
- new_fdt = (void *)addr_sp;
- debug("Reserving %zu Bytes for FDT at: %08lx\n",
- fdt_size, addr_sp);
- }
- #endif
- /* setup stackpointer for exeptions */
- gd->irq_sp = addr_sp;
- #ifdef CONFIG_USE_IRQ
- addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
- debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
- CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
- #endif
- /* leave 3 words for abort-stack */
- addr_sp -= 12;
- /* 8-byte alignment for ABI compliance */
- addr_sp &= ~0x07;
- #else
- addr_sp += 128; /* leave 32 words for abort-stack */
- gd->irq_sp = addr_sp;
- #endif
- interrupt_init();
- debug("New Stack Pointer is: %08lx\n", addr_sp);
- #ifdef CONFIG_POST
- post_bootmode_init();
- post_run(NULL, POST_ROM | post_bootmode_get(0));
- #endif
- gd->bd->bi_baudrate = gd->baudrate;
- /* Ram ist board specific, so move it to board code … */
- dram_init_banksize();
- display_dram_config(); /* and display it */
- gd->relocaddr = addr;
- gd->start_addr_sp = addr_sp;
- gd->reloc_off = addr – _TEXT_BASE;
- debug("relocation Offset is: %08lx\n", gd->reloc_off);
- if (new_fdt) {
- memcpy(new_fdt, gd->fdt_blob, fdt_size);
- gd->fdt_blob = new_fdt;
- }
- memcpy(id, (void *)gd, sizeof(gd_t));
- 这一段主要是在做内存分配动作.
- addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; 从RAM的顶部地址开始
- LOGBUFF_RESERVE : Reserving %dk for kernel logbuffer
- pram : Reserving %ldk for protected RAM
- tlb_addr: reserve TLB table(16K), 然后会把地址对齐到64K处.
- addr &= ~(4096 – 1): 做完上面的内存分配动作之后, 在保留4KB的空间. 干嘛用还不清楚.
- relocaddr: 在接来下的RAM就是U-boot可以用的了 : Top of RAM usable for U-Boot
- LCD
- 如果用户定义了CONFIG_FB_ADDR, 那这里就不为LCD保留显存了
- 如果用户定义了CONFIG_LCD, 又没有定义CONFIG_FB_ADDR, 则为LCD保留一块显存
- Code, data, bss : reserve memory for U-Boot code, data & bss
- malloc : reserve memory for malloc() arena, 突然想起了堆和栈的区别, 网上查了一下, 用户自己分配和释放的属于堆区. 貌似这个地方就是堆区哦.
- bd : Board Info struct
- gd: Global Data
- fdt
- irq_sp : IRQ & FIQ 栈指针. 向下生长
- start_addr_sp : 系统栈指针. 主要是在C函数调用过程中, 函数参数, 返回地址的入栈出栈操作.
- reloc_off : gd->reloc_off = addr – _TEXT_BASE;
- relocate offset, 这里的意思暂时没弄懂
- gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */ 这个赋值动作, 记得老版本的uboot是在BOARDDIR下面做的.
- dram_init_banksize : 需要在BOARDDIR下面实现, 告诉系统有几块RAM, 每一块的大小是多少.
init_sequence
[plain] view plain copy
- init_fnc_t *init_sequence[] = {
- arch_cpu_init, /* basic arch cpu dependent setup */
- mark_bootstage,
- #ifdef CONFIG_OF_CONTROL
- fdtdec_check_fdt,
- #endif
- #if defined(CONFIG_BOARD_EARLY_INIT_F)
- board_early_init_f,
- #endif
- timer_init, /* initialize timer */
- #ifdef CONFIG_BOARD_POSTCLK_INIT
- board_postclk_init,
- #endif
- #ifdef CONFIG_FSL_ESDHC
- get_clocks,
- #endif
- env_init, /* initialize environment */
- init_baudrate, /* initialze baudrate settings */
- serial_init, /* serial communications setup */
- console_init_f, /* stage 1 init of console */
- display_banner, /* say that we are here */
- #if defined(CONFIG_DISPLAY_CPUINFO)
- print_cpuinfo, /* display cpu info (and speed) */
- #endif
- #if defined(CONFIG_DISPLAY_BOARDINFO)
- checkboard, /* display board info */
- #endif
- #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
- init_func_i2c,
- #endif
- dram_init, /* configure available RAM banks */
- NULL,
- };
- arch_cpu_init
- 一般厂商会实现, 根据实际需要初始化CPU相关的一些东西
- 如果没有其他实现, 就会用board.c中的weak实现.
- mark_bootstage
- Record the board_init_f() bootstage (after arch_cpu_init())
- fdtdec_check_fdt : fdt相关, 暂不分析
- board_early_init_f
- 一般在BOARDDIR下面实现, 初始化必要的硬件
- timer_init
- 时钟初始化
- board_postclk_init
- get_clocks
- env_init
- 以nandflash为例, 此时还没有对nandflash进行初始化, 所以这个函数的实现不会从nand中去读取实际保存的变量. 只是简单标记env_valid为1
- env_common.c中的env_relocate() will do the real validation
- init_baudrate
- 初始化gd->baudrate
- gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE);
- serial_init
- uboot的serial子系统相关
- console_init_f
- stage 1 init of console
- Called before relocation – use serial functions
- print_cpuinfo
- display cpu info (and speed)
- checkboard
- display board info
- dram_init
- 初始化gd->ram_size
- 接下来的code会做内存分配, 需要用到这个值. 注意, 如果有多块DRAM, 起始地址不一样, 那这里的ram_size应该只是其中1块的大小.
- 比如BANK1: 0x20000000 64M; BANK2: 0x40000000 64M; 那这个ram_size应该是64M, 而不是128M. 具体原因, 可以看上文中的内存分配动作
board_init_r
[plain] view plain copy
- gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */
- 做过relocate之后, 才调用的board_init_r
[plain] view plain copy
- board_init(); /* Setup chipselects */
- board_init需要在BOARDDIR中实现.
[plain] view plain copy
- serial_initialize();
- uboot的serial子系统相关. 完成实际的串口初始化工作
[plain] view plain copy
- /* The Malloc area is immediately below the monitor copy in DRAM */
- malloc_start = dest_addr – TOTAL_MALLOC_LEN;
- mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);
- dest_addr是_main的汇编代码传递进来的参数 GD_RELOCADDR.
- GD_RELOCADDR是在board_init_f阶段被赋值的.
[plain] view plain copy
- power_init_board();
- 可以选择性的在BOARDDIR下实现.
- 如果没有实现, 则会使用board.c中的weak实现
[plain] view plain copy
- nand_init();
- nandflash初始化, 会调用mtd/nand/nand.c中的nand_init
[plain] view plain copy
- mmc_initialize
- sd卡初始化, 会调用drivers/mmc/mmc.c中的mmc_initialize
[plain] view plain copy
- env_relocate
- board_init_f->init_sequence中的env_init其实没有做什么实质性动作, 因为当时nand还没有初始化
- 所以这里的env_relocate会调用common/env_common.c中的env_relocate, 继而开始做实质性的env初始动作. 也就是从nandflash中读取我们自己设置的环境变量
[plain] view plain copy
- stdio_init
- 调用common/stdio.c中的stdio_init, 做一些IO相关的初始化
- lcd, video, keyboard, nc, jtag等等
[plain] view plain copy
- jumptable_init
- 调用include/_exports.h中定义的各种通用的操作函数
- get_version, getc, malloc, udelay等等
[plain] view plain copy
- console_init_r(); /* fully init console as a device */
- 控制台最终初始化
[plain] view plain copy
- #ifdef CONFIG_BOARD_LATE_INIT
- board_late_init();
- #endif
- 可以选择性的在BOARDDIR中定义board_late_init, 做一些最后的初始化动作
[plain] view plain copy
- #if defined(CONFIG_CMD_NET)
- puts("Net: ");
- eth_initialize(gd->bd);
- #if defined(CONFIG_RESET_PHY_R)
- debug("Reset Ethernet PHY\n");
- reset_phy();
- #endif
- #endif
- 在BOARDDIR中实现eth_initialize, 初始化网络
- 在BOARDDIR中实现reset_phy, 复位phy.
[plain] view plain copy
- for (;;) {
- main_loop();
- }
- 进入common/main.c中的main_loop
- 如果没有按下空格键, uboot会执行bootcmd中的命令
- 如果按下空格键, 则会进入控制台, 与用户交互, 执行命令
总结
stage1 vs stage2
stage1也就是SPL, stage2也就是本篇所指的u-boot.bin, 主要有以下不同点
- u-boot.bin在start.S中会对异常中断进行响应
- u-boot.bin中的board_init_f实现与SPL阶段不一样
- u-boot.bin中的board_init_r实现与SPL阶段不一样
global_data
u-boot.bin会对全局数据gd先清理, 然后在重新初始化.
需要实现的函数
- dram_init_banksize: BOARDDIR下实现
- 告诉系统有几块RAM, 每一块的大小是多少.
- board_early_init_f : BOARDDIR下实现
- board_init : BOARDDIR下实现
- eth_initialize : BOARDDIR下实现