基于nuvoton970学习 uboo2013 之 u-boot-spl
1、先来看看源码根目录下Makefile
打开Makefile 搜索 all: (默认的编译目标)
all: $(ALL-y) $(SUBDIR_EXAMPLES)
#
# 所以 默认目标是依赖 $(ALL-y) 和 $(SUBDIR_EXAMPLES)
在来看看 $(ALL-y), 在Makefile中搜索 ALL- :
# 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
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) += $(obj)$(subst ",,$(CONFIG_SPL_TARGET))
ALL-$(CONFIG_OF_SEPARATE) += $(obj)u-boot.dtb $(obj)u-boot-dtb.bin
首先这里 我使用的源码路径和编译路径一样,所以 $(obj) 的值为空,在Makefile中有如下定义:
# 如果 目标路径和源码路径不一样,obj 就等于目标路径,否则为空
ifneq ($(OBJTREE), $(SRCTREE))
obj := $(OBJTREE)/
src := $(OBJTREE)/
else
obj :=
src :=
endif
接着分析上面的ALL,从上面的定义可以看出, ALL-y 中一定要包含的有 u-boot.srec 、u-boot.bin和System.map。而u-boot-spl.bin是可选的。而我现在用的工程中配置过程中定义了CONFIG_NAND_U_BOOT,所以ALL-y中也包含了$(obj)u-boot-nand.bin。 在主目录下Makefile中搜索 $(obj)u-boot-nand.bin的定义:
$(obj)u-boot-nand.bin: nand_spl $(obj)u-boot.bin
cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin
从上面可以看出,u-boot-nand.bin 依赖 nand_spl 和 $(obj)u-boot.bin。
然后执行 cat 命令相对于把前两个文件合并成 u-boot-nand.bin 。而$(obj)u-boot.bin就是我们的主体u-boot.bin。此处暂不讨论,先分析nand_spl 。
nand_spl的定义如下:
nand_spl: $(TIMESTAMP_FILE) $(VERSION_FILE) depend
$(MAKE) -C nand_spl/board/$(BOARDDIR) all
nand_spl 依赖 $(TIMESTAMP_FILE) 时间戳文件和$(VERSION_FILE)版本文件。
TIMESTAP_FILE = $(obj)include/generated/timestamp_autogenerated.h
VERSION_FILE = $(obj)include/generated/version_autogenerated.h
$(VERSION_FILE):
@mkdir -p $(dir $(VERSION_FILE))
@( localvers='$(shell $(TOPDIR)/tools/setlocalversion $(TOPDIR))' ; \
printf '#define PLAIN_VERSION "%s%s"\n' \
"$(U_BOOT_VERSION)" "$${localvers}" ; \
printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' \
"$(U_BOOT_VERSION)" "$${localvers}" ; \
) > $@.tmp
@( printf '#define CC_VERSION_STRING "%s"\n' \
'$(shell $(CC) --version | head -n 1)' )>> $@.tmp
@( printf '#define LD_VERSION_STRING "%s"\n' \
'$(shell $(LD) -v | head -n 1)' )>> $@.tmp
@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
$(TIMESTAMP_FILE):
@mkdir -p $(dir $(TIMESTAMP_FILE))
@LC_ALL=C date +'#define U_BOOT_DATE "%b %d %C%y"' > $@.tmp
@LC_ALL=C date +'#define U_BOOT_TIME "%T"' >> $@.tmp
@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
#
# 根据编译时间动态的创建时间戳文件和版本文件,不做过多分析。
#
重点是:$(MAKE) -C nand_spl/board/$(BOARDDIR) all
源码路径下 config.mk 有:
ifdef VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif
include/config.mk 中有:
BOARD = nuc970evb
VENDOR = nuvoton
所以 BOARDDIR = nuvoton/nuc970evb。因此上面的命令是:
make -C nand_spl/boatd/nuvoton/nuc970evb all
make -C 命令是执行指定路径下的Makefile,所以就继续分析nand_spl/boatd/nuvoton/nuc970evb/Makefile。打开Makefile,搜索 all 的定义:
all: $(obj).depend $(ALL)
同上,在 nand_spl/boatd/nuvoton/nuc970evb/Makefile 中查找 ALL 的定义:
nandobj := $(OBJTREE)nand_spl/
ALL = $(nandobj)u-boot-spl $(nandobj)u-boot-spl.bin $(nandobj)u-boot-spl-16k.bin
可以看出默认会编译出u-boot-spl、u-boot-spl.bin和u-boot-spl-16k.bin三个镜像。在Makefile中搜索三个镜像的定义:
$(nandobj)u-boot-spl-16k.bin: $(nandobj)u-boot-spl
$(OBJCOPY) ${OBJCFLAGS} --pad-to=$(PAD_TO) -O binary $< $@
$(nandobj)u-boot-spl.bin: $(nandobj)u-boot-spl
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(nandobj)u-boot-spl: $(OBJS) $(nandobj)u-boot.lds
cd $(LNDIR) && $(LD) $(LDFLAGS) $(__OBJS) \
-Map $(nandobj)u-boot-spl.map \
-o $(nandobj)u-boot-spl
可以看出 u-boot-spl.bin 和 u-boot-spl-16k.bin 都是从u-boot-spl 使用$(OBJCOPY) 过来的,区别是u-boot-spl-16k.bin在生成的过程中有个对齐。从生成的结果讲,u-boot-spl 比 u-boot-spl.bin和u-boot-spl-16k.bin都要大,u-boot-spl.bin和u-boot-spl-16k.bin刚好一样大,也就是u-boot-spl.bin本身就是按照$(PAD_TO) 对齐的。
所以现在重点分析: $(nandobj)u-boot-spl 。
$(nandobj)u-boot-spl 依赖于 $(OBJS) 和 $(nandobj)u-boot.lds,前者就是编译出的目标文件(.o文件), 后者是链接脚本,u-boot-spl 就是把 $(OBJS) 按照链接脚本链接成一个目标文件,链接指令:
$(LD) $(LDFLAGS) $(__OBJS) -Map $(nandobj)u-boot-spl.map \
-o $(nandobj)u-boot-spl。
-Map 输出链接map到文件中, -o 输出选项。
在 nand_spl/boatd/nuvoton/nuc970evb/Makefile 搜索 LDFLAGS :
LDFLAGS := -T $(nandobj)u-boot.lds -Ttext $(CONFIG_SYS_TEXT_BASE) $(LDFLAGS) \
$(LDFLAGS_FINAL) -gc-sections
在 nand_spl/boatd/nuvoton/nuc970evb/Makefile 搜索 __OBJS :
# SOBJS 的源文件为汇编文件,.S文件
#
SOBJS = start.o cpu_init.o lowlevel_init.o divlib.o _lshrdi3.o _ashldi3.o _adhrdi3.o
# COBJS 的源文件为C文件, .c文件
#
COBJS = nand_boot.o nand_ecc.o nuc970_nand.o nuc970_nand_spl.o nand.o nand_bbt.o nand_ids.o nand_util.o nand_base.o serial_nuc970.o serial.o nuc970_sysprintf.o nuc970_wdt.o timer.o .........
# 省略一些 .o 比较多
# 把SOBJS 和 COBJS 的名字换成源文件的名字
SRCS := $(addprefix $(obj), $(SOBJS:.O=.S) $(COBJS:.o=.c))
# 把COBJS和SOBJS加上路径名
OBJS := $(addprefix $(obj), $(SOBJS) $(COBJS))
__OBJS := $(SOBJS) $(COBJS)
到这里 u-boot-spl需要的源文件可以完全知道了,还有一步链接文件 u-boot.lds。
LDSCRIPT= $(TOPDIR)/nand_spl/board/$(BOARDDIR)/u-boot.lds
$(nandobj)u-boot.lds: $(LDSCRIPT)
$(CPP) $(CPPFLAGS) $(LDFLAGS) -ansi -D__ASSEMBLY -P - <$^ >$@
nand_spl/boatd/nuvoton/nuc970evb/Makefile 中还有类似
$(obj)start.S:
@rm -f $@
@ln -s $(TOPDIR)/nand_spl/board/nuvoton/nuc970evb/start_nand_spl.S $@
$(obj)lowlevel_init.S:
@rm -f $@
@ln -s $(TOPDIR)/arch/arm/cpu/arm926ejs/nuc970/lowlevel_init.S $@
.....
#
# 建立软链接到合适的源文件
#
#
# 熟悉的编译规则,.S编译成.o 和.c编译成.o的规则动作。
$(obj)%.o: $(obj)%.S
$(CC) $(AFLAGS) -c -o $@ $<
$(obj)%.o: $(obj)%.c
$(CC) $(AFLAGS) -c -o $@ $<
Makefile分析到这里结束,接下来是源码分析。
2、源码分析
从nand_spl/board/nuvoton/nuc970evb/Makefile 中可以看出
start.S ----> nand_spl/board/nuvoton/nuc970evb/start_nand_spl.S,所以开始代码从这里分析:
.global _start
_start:
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
/* 省略一些代码 */
......
......
.global _TEXT_BASE
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE
.global _bss_start_ofs
_bss_start_ofs:
.word __bss_start - _start
.global _bss_end_ofs
_bss_end_ofs:
.word __bss_end__ - _start
.global _end_ofs:
_end_ofs:
.word _end - _start
/* 省略一些代码 */
.....
reset:
/* ARM芯片有7中工作模式:
* 1、用户模式(User):用于正常执行程序
* 2、快速中断模式(FIQ):用于高速数据传输
* 3、外部中断模式(IRQ):用于通常的中断处理
* 4、管理模式(SVC):操作系统使用的保护模式
* 5、数据访问终止模式(abt):
* 6、系统模式(sys):运行具有特权的操作系统任务
* 7、未定义指令终止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件
*/
/* set the cpu to svc32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
call_board_init_f:
/* 设置堆栈指针 */
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7
ldr r0,+0x00000000
/* 跳转到board_init_f */
bl board_init_f
Arm的工作模式切换有两种方法:
被动切换:在arm运行的时候产生一些异常或者中断来自动进行模式切换
主动切换:通过软件改变,即软件设置寄存器来经行arm的模式切换,应为arm的工作模式都是可以通过相应寄存器的赋值来切换的。
Tips:当处理器运行在用户模式下,某些被保护的系统资源是不能被访问的。
=======================================================================================
board_init_f 在 nand_spl/board/nuvoton/nuc970evb/nuc970_nand_spl.c中定义:
void board_init_f( unsigned long bootflag )
{
writel( read(REG_PCLKEN0) | 0x10000, REG_PCLKEN0 ); /* UART clk*/
writel( read(REG_PCLKEN0) | 0x100, REG_PCLKEN0 ); /* Timer clk */
nuc970_serial_initialize();
nuc970_serial_init();
printf("nand_boot\n");
nand_boot();
}
board_init_f函数中打开了UART和Timer的时钟,做了串口的初始化,然后跳转到nand_boot();
nand_boot函数定义在nand_spl/board/nuvoton/nuc970evb/nand_boot.c
void nand_boot( void )
{
struct nand_chip nand_chip;
nand_info_t nand_info;
__attribute__((noreturn)) void (*uboot) (void);
/* 初始化nand */
nand_chip.select_chip = NULL;
nand_info.priv = &nand_chip;
nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = (void __iomem*)CONFIG_SYS_NAND_BASE;
nand_chip.dev_read = NULL;
nand_chip.options = 0;
board_nand_init(&nand_chip);
if (nand_chip.select_chip)
nand_chip.select_chip(&nand_info, 0);
if (nand_scan(&nand_info, 1))
return;
nand_register(0);
nand_load( &nand_info, CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE,
(uchar*)CONFIG_SYS_NAND_U_BOOT_DST);
if ( nand_chip.select_chip)
nand_chip.select_chip( &nand_chip, -1 );
/* jump to u-boot image
*/
uboot = (void*)CONFIG_SYS_NAND_U_BOOT_START;
(*uboot)();
}
到这里u-boot-spl.bin结束,跳转到u-boot.bin。
Tip:
nuc972内部集成64MB的DDR2,所以代码中未看到初始化SDRAM的过程。
|