之前买了块开发板,用来学习嵌入式的东西。后来自己制作了自己的交叉编译工具,然后编译了最新的u-boot-1.1.6,烧到板子里后启动不起来,这才知道搞bootloader的移植,不是这么容易的。而且要想调试方便,就得买一个仿真器。
好多人都建议我在上层开发,别管底下的东西,可是我觉得,既然买了这块板子,就要从头到尾了解它,驱动程序这块可以暂时不管,但是与硬件相关的启动部分不能不管,如果这块也不管的话,那就跟在linux环境下写普通程序,没什么太大的差别了。当然,这只是我现阶段的理解,也许以后这个理解还会升华。
由于我的开发板是一块仿三星S3C2440的板子,这个板子提供的CPU是ARM9的,核心是ARM920T,所以我就需要买一个能调试ARM9芯片的仿真器,从网上比较来,比较去,花了1千多买了一个,现在为止,投资在嵌入式上的钱,有3K多了,什么时候才能产生回报啊,哈哈哈哈
我到目前为止,都深信我的交叉编译工具制作的没有问题,现在首先要做的,就是用它编译一个比较新版本的u-boot出来,调试成功后烧到我的板子上,让它来启动我的linux内核。如果成功,那下一步就是再编译出我自己的linux内核,替换掉板子里自己带的那个。如果我能完成这两项工作,我的嵌入式开发就将能系统的展开了,哈哈:)
首先做的就是第一步,基本读懂u-boot的编译脚本,读弄与移植有关的代码。
下面就来分析u-boot-1.1.6的Makefile的内容,按步骤分析,把我认为重要的写出来,希望有不明白的人,搜到我的博客,能搞明白你想知道的问题。由于我是做ARM920T的移植,所以只分析跟我搞的事情相关的东西:)
______________________________________________________________________
VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 6
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h
HOSTARCH := $(shell uname -m | /
sed -e s/i.86/i386/ /
-e s/sun4u/sparc64/ /
-e s/arm.*/arm/ /
-e s/sa110/arm/ /
-e s/powerpc/ppc/ /
-e s/macppc/ppc/)
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | /
sed -e 's//(cygwin/).*/cygwin/')
export HOSTARCH HOSTOS
# Deal with colliding definitions from tcsh etc.
VENDOR=
______________________________________________________________________
这部分是常量的定义,注意这里的$(obj),这个常量在这里为空,它放到这里,是因为在target unconfig, clean, clobber, distclean中需要用这个变量,具体是个怎么用法,我没有去仔细分析。
HOSTARCH这个变量的赋值,是通过执行一套shell程序来完成的,其中$(shell xxx)的语法就是在shell中执行xxx的命令。这里的|就是linux中的管道处理符,/就是换行的连接符,表示下一行与本行是同行程序处理。
uname -m,就是得到machine hardware name,中文翻译成机器硬件名?!
sed -e的意思,就是表示后面跟的是一串命令脚本,s/abc/def/的命令表达式,就是表示要从标准输入中,查找到内容为abc的,然后替换成def。这样执行这一套程序下来,就知道了机器的硬件体系了。
uname -s,得到kernel name,内核的名字。
tr '[:upper:]' '[:lower:]',表示从标准输入里,把大字都换成小写,然后输出到标准输出。
然后后面再跟了一串用来特别处理cygwin环境下编译的环境变量的配置的。
export的意思,我应该不用详细说了吧。
________________________________________________________________________
ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif
ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD := 1
export REMOTE_BUILD
endif
# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src
___________________________________________________________________________
这段前面,首先使用了一个条件判断,判断0这个变量有没有被定义,如果定义了,则进一步判断,这个0的定义是从哪里来的,$(origin 0)就是来得到0的定义的来源,如果是命令行,则采用。
后面一段也差不多,如果BUILD_DIR变量不为空,则给另外一个变量saved-output赋值。
执行shell的时候,用了一个||符号,这个符号的功能与C语言里的||符号功能很相似,如果左边的值为真,则不执行后面的操作。所以这里就是先用-d选项判断${BUILD_DIR}这个目录存在与否,如果存在,就不执行后面的建目录的命令,如果不存在,则建目录。
后面一段就是执行cd命令,进入到新建的目录里,然后执行pwd命令来得到当前目录的真实位置。为什么需要这样做呢,因为前面的创建目录工作可能不成功,所以导致后面的cd命令也没有进去,所以需要后面的pwd命令来确认一下。接下来的if用了一个三段式的形式,if(a,b,c)这样的形式,执行步骤为,先判断a的真假,如果为真,则执行b,如果为假,则执行c。所以这里的意思就是判断目录建成没有建成,如果建成,则什么也不干,没建成,就使用error,输出错误信息且退出。
再后面一段就是普通的赋值,也没什么难的,注意第一行OBJTREE变量的赋值,也是用了if的三段式的操作
MKCONFIG变量,这个变量比较重要,以后再说,这里就暂且记住它代表的文件的位置,就是源码根目录下的mkconfig文件。
____________________________________________________________________________
ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk)),这句非常重要,影响到了后面的整个逻辑的走向,中间的内容暂时忽略。
这句的封闭语句为
else
all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin /
$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot /
$(SUBDIRS) version gdbtools updater env depend /
dep tags ctags etags $(obj)System.map:
@echo "System not configured - see README" >&2
@ exit 1
endif
首先,它判断$(OBJTREE)/include/config.mk与$(wildcard $(OBJTREE)/include/config.mk)得到的值是否一样。$(wildcard PATTERN)的使用就是查找到与PATTERN相符合的,并且是存在的,以空格分开的文件列表。说白了,这句话就是判断$(OBJTREE)/include/config.mk文件是否存在。如果存在,则执行咱们忽略的内容,如果不存在,则执行咱们没有忽略的内容。
先看不存在的情况。不存在的时候,就把所有目标,都指向到一条依赖上了
@echo "System not configured - see README" >&2
@ exit 1
然后不管执行make xxx命令,都会显示System not configured - see README,然后它把这个输出输到标准错误输出文件里了,退出了执行。
所以这里就明说了,如果$(OBJTREE)/include/config.mk文件不存在,则编译不能进行下去了。
那如何生成$(OBJTREE)/include/config.mk这个文件呢,请看我的u-boot-1.1.6源码浅析(二)。