nuvoton uboo2013引导流程 2 - spl

it2026-08-30  3

基于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的过程。

最新回复(0)