1、从uboot官方网站下载uboot


2、uboot并不越新的版本越好,早期的uboot版本号是类似于1.3.4xx这样的,现在的是类似于2009xxx的是用日期的。

uboot版本越新支持的开发板和CPU就越多,代码量就越多,如果我们的CPU和开发板的并没有这么多东西,那我们可以不用新的。而用合适我们的,一般结合开发板处来的日期,和uboot的日期来结合找到合适的uboot。

3、一般情况下,uboot的common目录下,和drivers目录下,fs文件系统目录下等一些是不会有很多变化的,变化最大的就是board目录下和CPU目录下,因为这目录下的内容都是和CPU和开发板相关的,不同的CPU开发板,当然差别会很大了


4、新版uboot配置体系的改变

(1)uboot的版本从2013.10到2014.10中的某个版本开始,配置体系发生了很大的变化,就是uboot中引入了Linux内核的配置体系(ubuild kconfig memuconfig),从而让我们可以在图形界面下,像配置内核一样配置uboot,所以新版本的uboot在配置时,就和我们之前将的不一样了,但是却和Linux内核配置是差不多的了。所以当我们会了Linux内核的配置体系后,对这个新版本的uboot的配置体系也就一下子明白了。


5、我们选择的从uboot官方的uboot2013.10版本的uboot开始移植

(1)下载后,在Linux中解压后,发现文件结构和我们之前的很相似,不同的就是lib ,以前的有很多lib,lib_arm、lib_genricl等等,这个uboot中有lib和arch,以前lib_arm中放的是跟arm相关的架构的库,lib_genric中放的通用的库,现在的arch中放的架构相关的,lib中放的通用的库。

(2)我么要在这个uboot中选择一个和我们开发板最接近的一个配置头文件,和一个board下的开发板。首先我们应该找到一个和我们的CPU相同的配置头文件,所以我找到的是在include/configs下的s5p_goni.h这个头文件,因为打开后发现宏定义是定义了S5PC110的,我们的CPU就是这个。所以通过这个CPU,我们找到了对应的用这个CPU曾经开发过的开发板,就是在board目录下的goni这个对应的开发板。


6、删除一些无关紧要的文件,要明确自己选择的CPU,开发板进行删除,CPU的选择的是哪一个,更进一步可以看makefile中给mkconfig脚本传参的时候,传的参数决定,在我们的这个新板的uboot中,在makefile中可能找不到,可能是在根目录下的boards文件中。


7、我们先从makefile中开始,看到了%_config:: unconfig

@$(MKCONFIG) -A $(@:_config=)

说明我们在配置的时候,make xxx_config后就会找到这个目标,%是通配符,之后调用了这个mkconfig脚本,传进去的参数$1是-A,$2是xxx。

之后,就开始看mkconfig这个脚本,这个脚本的24行到25行,

if [ \( $# -eq 2 \) -a \( "$1" = "-A" \) ] ; then

# Automatic mode

line=`awk '($0 !~ /^#/ && $7 ~ /^'"$2"'$/) { print $1, $2, $3, $4, $5, $6, $7, $8 }' boards.cfg`

if [ -z "$line" ] ; then

echo "make: *** No rule to make target \`$2_config'.  Stop." >&2

exit 1

fi


set ${line}

# add default board name if needed

[ $# = 3 ] && set ${line} ${1}

fi


意思就是看我们传进来的参数是否是对的,如果是对的话,就会在解析这个用这个awk正则表达式,去boards.cfg中找跟我们我传进来参数$2(s5p_goni),有这个字符串的那一行赋值给line这个变量,完之后,将$1到$8改成boards.cfg那一行的参数。达到我们以前的那种make smdkv210single_config时,makefile中关于这个目标传进去参数的效果

Active  arm         armv7          s5pc1xx     samsung         goni                s5p_goni                             -    这些是将来新的$值,从1到8

(2)makeflie 中COSS_COMLINE这一行,没有交叉编译工具,我们要在makefile中添加一个交叉编译工具

(3)之后我们make distclean make s5p_goni_config make 配置编译一下,看能否编译通过,是否生成了u-boot.bin



8、如何烧录官方的uboot

(1)烧录uboot有两种方法,一种是在Linux下用dd命令烧录到SD卡上,一种是在windows下用专用的烧录工具。

不建议用windows下的烧录工具,因为出了问题不好解决,我们用在Linux的dd命令,就是用dd命令将要烧录的镜像写入到SD卡的扇区

(2)所以,就需要移植原来我们的uboot中的那个sd_fusing文件夹到我们uboot官方的目录下,用这个sd_fusing文件中的sd_fusing脚本来进行烧录。

(3)当我们直接烧录进去后,发现串口打印出来了四行信息,两个SD检查错误,说明IROM的BL0代码在SD0通道时,inand中没有uboot,所以打印出来的,第二个是在第二顺序,也就是从外部SD2启动时,打印出来的,说明我们SD2中的uboot校验和没有通过,经过好的uboot和这个uboot,用二进制文本工具打开后,发现这个新的uboot中的钱16B不是空的,也就是说在sd_fusing中的那个C110那个校验uboot的头信息时,错误了,因为那个校验文件是把uboot前16B,当做空进行处理的,所以在新的uboot中的start.S中开始的位置,加上.word 0x20000 .word 0x0 ,word 0x0 .word 0x0 后就好了。

这时在运行时,只打印出来了一个SD检查错误,说明外部的SD2中的是校验通过的。


9、分析官方uboot的start.S代码

(1)复位后开始的第一句代码是bl save_boot_params 经过追中这个函数是被一宏声明为全局的,并且是一个空函数。直接返回了。

(2)当我们在uboot中索引一个函数的时候,发现在还几个文件中都有的时候,如果你无法确定到底是哪一个文件在最后的编译过程包含了进来,到底哪一个文件才是我们想要的那个文件的时候,我们就看这个文件的目录下的makefile中是否包含了这个文件,如果makefile中包含了这个文件,那么就是这个文件。辅助验证的方法就是,在你编译过后,你在看你的那个文件是否被编译成了.o文件,就可以确定这个文件是否被包含进makefile中,是否是我们的工程真正需要的了



总结:uboot移植的时候,如果是从官方网站下载的uboot的话

我们要选择一个合适的uboot版本,最好是我们的CPU的日期是在这个uboot日期之前的,这样我们的CPU,uboot中才会有:在选定好后,要删除一些没有用的文件,让我们SI的工程索引的文件数变少,好分析代码。可以通过makefile和mkconfig脚本来确定我们我们改留下那些文件,就是确定我们的CPU是什么,之后就可以找到相应CPU曾经被别人移植过的开发板,我们这跟着两个相关的东西文件留下。找到别人用我们CPU移植过的开发板后,我们以这个开发板为篮板,来移植到我们的开发板上。官方的uboot中是没有sd_fusing这个烧写SD卡的文件的,我们要移植一个烧录SD上的东西,也就是我们以前用过的sd_fusing文件夹,在Linux下进行uboot的烧录,我们将uboot配置编译好了以后,烧录到SD卡中,运行一下看运行的结果是什么样的,在一点一点的分析代码和实践现象和移植。


u-boot contains relocations other than R_ARM_RELATIVE错误

用grep "R_ARM_RELATIVE" nR *去追踪,发现是在makefile中,直接将他屏蔽



(2)在官方uboot中,有一个cpu_is_s5pc110这么函数,没有找到,原因是这个函数是被一宏用cpu_is_##xxx来决定的。

#define IS_SAMSUNG_TYPE(type, id) \

static inline int cpu_is_##type(void) \

{ \

return s5p_cpu_id == id ? 1 : 0; \

}

这么办到的,所以直接找函数会找不到



10、由于官方的2013.10的uboot中,时钟的配置和我们板子的不一样,所以我们把官方uboot的时钟部分的代码删除掉了,自己根据三星提供的uboot,将里面的时钟初始化的代码移植到了,我们的这个官方uboot中,宏定义什么的都移植好了以后,在编译的时候,发现错误了,发现可能是因为gcc的一些bug导致的,可能换个uboot就好使了,但是我们并没有那么做,我们把时钟初始化那部分的代码,涉及到寄存器间接寻址的地方,我们直接把寄存器的地址算出来以后,直接用直接寻址的方式来弄,发现就好了。

。。。。。。但是后来发现居然时钟唉lowlevel_init.S函数中没有加配置头文件导致的,所以我们定义的宏没有找到。。。所以说上面的话。。。。。



11、在2013.10uboot版本中,关于MACH_TYPE机器码,在以前的uboot中,比如1.3.4中,这个机器码是被定义在配置头文件中的,但是在2013.10新板的uboot中,这个机器码是统一集中的定义在一个arch/arm/asm/lib/mach_types.h中的,每一个CPU所对应的开发板如果像Linux内核管理者申请了这个CPU的机器码,Linux内核管理者就会给一个机器码,并且在这个头文件中包含这个CPU所对应所对应的开发板的机器码的宏。


12、

这个版本中的uboot里有onenand,我们的是没有,所以我们要去掉。通过去掉配置头文件的宏,但是还会引发一些其他的关联性错误,比如环境变量env的操作,在不同的介质中,操作方法是不一样的。因为去掉了这个onenand的宏,所以将来在找env_xxx.c的时候,env_onenand就不会被找到,所以在makefile中,这个文件也不应该在makefile中出现了。

在commond目录下,有些关于环境变量env_xxx.c的文件中,这个目录下,有个makefile,这个makefile中决定了什么宏就链接哪个env_xxx,根据这个宏,在SI中进行搜索找到这个宏定义的地方,定位后,把跟onenand相关的宏屏蔽掉。发现编译时说必须要定义一个env_xxx,所以在common目录下找到与我们相关的启动介质的env操作文件所对应的宏,在配置头文件中定义为1.

发现还有问题,问题是在env_mmc.c中的108行的一个CONFIG_SYS_MMC_ENV_DEV宏没定义,发现这个宏是mmc设备的号,所以我们在配置头文件中将这个宏定义成0,代表设备是SD卡通道0,就是inand,如果是1代表SD卡通道2,就是SD卡。

之后发现能进入到命令行下了。



13、SD/MMC驱动浏览

(1)先从初始化代码进行浏览,board.c中的mmc_initialize函数,进去后

drivers/mmc/mmc.c 

drivers/mmc/sdhci.c s5p_sdhci_initSD卡控制器的初始化

arch/arm/include/asm.arch-s5pc1xx/mmc.h


(2)SD卡发生错误的路径

arch/arm/lib/board.c中

mmc_initialize函数

@1:rivers/mmc/mmc.c中

@2: do_preinit函数

drivers/mmc/mmc.c中

@3: mmc_start_init

drivers/mmc/mmc.c中

@4: mmc_go_idle

@5: mmc_send_cmd

@6: mmc->send_cmd函数指针

drivers/mmc/sdhci.c

函数指针指向了这个函数

@7: sdhci_send_command

@8: sdhci_transfer_data

这个函数发生了错误,错误信息也是从这里打印出来的


(3)drivers/mmc/sdhci.c中的函数,构成了三星210CPU中所有的SD/MMC控制器的驱动,这些函数是用来210CPU内部的SD/MMC控制器和外部的SD卡通信的

(4)sdhci_transfer_data这个函数出错了,说明我们的SOC内部的SD/MMC控制器和我们外部的SD卡(我们的代码中用的是SD通道0的inand)的数据传输出现了问题(细节分析发现是我们控制器内部有一个中断状态错误被置位了)

解决方案分析:

三条思路:第一条思路,看这个函数的每一条语句的实现过程(要对SD卡的通信,和SOC的SD/MMC控制器非常了解) ,然后发现错误所在,然后修改代码,第一种方法就跟些驱动一样了。第二种方法就是,把原来三星移植好的uboot中的SD/MMC驱动整个移植过来,替换掉这个2013.10版本中的mmc驱动。第三条思路就是参考三星版本移植好的uboot中的SD/MMC驱动,来实现修补我们的这个2013.10版本的uboot中的SD/MMC驱动。


14、SD卡驱动移植1

(1)uboot2013.10版本中SD卡驱动相关的文件主要有:

drivers/mmc/mmc.c

drivers/mmc/s5p_sdhci.c

drivers/mmc/sdhci.c

board/samsung/goni/goni.c辅助文件


(2)三星移植的uboot中SD卡驱动相关的文件主要有:

drivers/mmc/mmc.c

drivers/mmc/s3c_hsmmc.c

drivers/mmc/mmc.c

cpu/s5pc11x/cpu.c

cpu/s5pc11x/setup_hsmmc.c


(3)分析发现,要想SD卡驱动工作,有两部分,一部分是SD/MMC的驱动文件,一部分是uboot本身提供的代码(GPIO的初始化,SD/MMC控制器的时钟的初始化)

(4)我们将三星的uboot中的mmc.c、s3c_hsmmc.c和cpu.c中关于初始化MMC控制器那部分的代码拷贝到我们的uboot2013.10版本中,相关的makefile进行修改,相应的宏进行定义,进行移植。

(5)同时将drivers/include/mmc.mmc.h和s3c_hsmmc.h这两个个文件也移植到我们uboot2013.10中。

(6)编译后出错:cmd_mmc.c中出现了错误,原因是cmd_mmc.c文件和mmc驱动密切相关,我们的驱动mmc改成了三星的那个了,那么我们的cmd_mmc.c也要跟着改成三星的那个才行。

(7)编译后还是出现,错误在mmc_writer.c中,我们的uboot2013.10中可能原先有用到这个文件,所以在drivers/mmc目录下的makefile中有这个文件,但是因为我们移植了三星uboot中的那个SD/MMC驱动体系,在这个体系中没有用到这个mmc_writer.c文件,所以我们在uboot2013.10版本中,也要在drivers/mmc目录中的makefile里把这个文件拿掉,注释掉就行

(8)编译后还是穿线了错误,错误是没有找到reg.h这个头文件,因为在三星的uboot中,这个文件应该是被mkconfig脚本中用符号链接的方式,将s5pc110.h这个头文件符号链接成了reg.h,所以我们在uboot2013.10版本中移植过来了三星的MMC驱动时,没有这个头文件,因为uboot2013.10版本中没有创建者个符号链接,所以我们屏蔽掉着个头文件,加上s5pc110.h这个头文件就行

(9)至此SD/MMC的驱动就成功的移植到uboot2013.10中,在我们开发板上运行也可以控制SD/MMC通道0和通道1了。


效果测试:

在uboot底下,用md读取内存、mw写内存、mmc read 命令读通道0和通道1的设备内容到内存中去观察、mmc write命令将内存中的内容写入到mmc设备中,在读到内存中进行效果测试。测试发现成功了。


15、环境变量的移植

(1)虽然因为SD/MMC的驱动移植好了,环境变量已经可以保存到MMC设备上了,可以工作了,但是我们还是要搞清楚环境变量env被放在哪个位置了,应该放在哪个位置。会不会放在了一个别人用的地方,uboot放的地方,内核放的地方,或者其他别人用的地方。

(2)所以要看我们代码中的分区表

(3)环境变量不能放在一些其他人用的地方,我们SD2的1-16扇区和49-x(x-49大于uboot的大小)扇区中放的是uboot。我们可以选择将环境变量放在内部的inand也就是SD0上,也可以放在外部的SD卡也就是SD2上。

(4)从SD卡的烧录脚本中可以看出来,我们的uboot在SD卡中的分布是,SD2通道的SD卡上的第0扇区是空闲的,第1-16扇区是被uboot的BL1占用的,第17-48扇区是空闲的,第49-x扇区是放uboot的BL2的,也就是整个uboot,在往后应该放的是kernel、rootfs等,所以我们环境变量不应该放在这些已经有东西的扇区位置,其他位置都可以商量,我们环境变量有16KB大小,也就是32个扇区大小。是在配置头文件中知道的。

(5)我们目前的情况是,当我们save的时候,环境变量是保存到SD0中,也就inand中,而我们的uboot在SD2中,也就是SD卡上,而我们的inand现在还没有用,所以我们的环境变量现在是不根本不会覆盖什么东西的,怎么可能覆盖到SD卡上呢。因为代码现在是保存到SD0上的,而SD0我们还没用,但是将来我们是要烧录uboot、kernel、rootfs等到inand中的,所以我们也应考虑一下环境变量在inand中的位置,将来在烧录其他东西的时候也要考虑其他东西的部署位置,这就是对inand的部署。

(6)分析saveenv函数代码,找到env环境变量写入到SD/MMC设备的哪个设备,环境变量写入到该设备的相对于扇区0的偏移量。找到了COFIG_ENV_OFFSET这个宏,决定了相对于扇区0的偏移量,经过分析,这个宏的值是0,所以我们env被写入到了inand中的0-32个扇区中了。

(7)#define MOVI_BL2_POS ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_BL1_BLKCNT + MOVI_ENV_BLKCNT)

这个宏告诉我们了,我们uboot的BL2是放在了SD卡的扇区49开始的位置。 1 + 16 +32

分析可以知道,1就是扇区0,是空闲的,16就是BL1放的位置1-16扇区,32就是17-48放环境变量的位置。49自然就uboot的BL2开始扇区了。所以我们将env放在17扇区其实的位置就行


16、如何测试我们环境变量保存的位置是否正确

(1)首先我们要保证我们的inand中之前没有环境变量,不然怎么知道我们这次的是否保存好了呢,所以我们要先在uboot下将我们要放环境变量的扇区位置给写乱,将我们内存中的可用地址中内容写入到inand中的0到48扇区,这样程序在重inand中进行CRC校验环境变量的时候,肯定是错误的,如果我们将环境变量成功的保存到了这个17-48扇区的位置的话,在开机的时候,CRC校验就会通过,程序就不会使用默认的环境变量。则证明是成功的。在uboot下用 mmc writer 0 30000000 0# 49来写乱我们的inand中的0到48扇区。



17、网卡驱动的移植

(1)运行uboot后发现找不到网卡,所以根据错误信息的追踪发现,是因为我们没有将我们网卡注册到网卡驱动上,网卡驱动这个链表上没有网卡,所以我们要添加DM9000的注册函数,将我们网卡注册到网卡驱动上,也就是注册到网卡驱动的链表上。可以用那个空的函数board_eth_init中进行代码的添加。









一、自我总结的uboot移植的思维导图

注意宏,始终要记得uboot第一阶段完成的关键任务。重要的就是初始化串口(方便调试),初始化DDR,重定位。

出现了问题

如果串口不好使

用LED代码跟踪调试出现错误的地方

如果串口好使

用串口调试跟踪代码出现错误的地方

注意uboot第一阶段要做的任务,和uboot第二阶段要做的任务

将uboot的第一阶段先调试好

关键的是串口的初始化,DDR的初始化,重定位,如果uboot没有,那就参照其他的uboot移植

将DDR初始化好,确认没问题后,进行重定位。

还要注意的是,代码的执行顺序,因为BL1的代码只会读取前8K或者前16K,所以要保证在前面的这些代码中要将DDR的初始化和重定位等都完成好,所以如果代码的执行顺序不对,可能会出现改执行的代码在前8K的代码当中没有被执行,所以要看链接脚本对不对,代码段的顺序对不对

之后去第二阶段初始化板级的。