uboot移植-从uboot官方源码开始移植过程总结

时间:2022-06-14 08:10:48

1 选取源码 下载源码 解压源码

自行登录uboot官网,下载uboot源码,我选取的是2013.10的,因为之后的源码采用类似新的配置模式

(能用即可)。我的源码是uboot-2013.10,解压缩一份在windows(用来查看分析和修改),一份在

Linux(用来交叉编译)。

2 寻找目标板子

uboot工程里面有很多开发板,我们要找到一个和我们类似SOC的开发板,很显然最起码要匹配两个东

西才可以,一个是SOC产商,一个是SOC(Samsung和s5pv210)。寻找完毕,过程略(产商是三星,

开发板选择三星的goni)。

3 建立工程

在windows下利用sourceInsight角建立过程。这份代码只是用来分析和修改,不参与编译,所以为了分

析索引的方便,我们删除一些不需要的文件。比如我们的SOC是三星公司的s5pv210,所以其他类型的

板子完全可以删掉,在比如我们的架构是ARM,像PPC,MIPS,X86等架构的文件夹完全可以删除掉。还有

一些其他的,不知道要不要删的就不删(就是分析的时候多了些)。好了,现在建立完毕。

3 编译源码

在Linux下先编译源码,那么问题来了,就是编译工具,我们打开主makefile索引到CROSS_COMPILE

修改其为自己的交叉编译工具即可。开始编译,看看原版的运行效果(肯定不能运行的拉拉,不过我们

要从现象开始分析修改),好了,执行

make distclean

make s5p_goni_config

make

不出意外,已经生成了uboot.bin。

4 烧录

怎么烧录了?我们可以使用之前三星做好的脚本烧之,那家伙就是sd_fusing。复制到现在的uboot工程

目录下即可。呐,插卡,运行脚本./sd_fusing.sh /dev/sdx 完成烧录。

5 运行看效果

运行,这时你会看到CRT终端上打印了两个SD CHENKSUM ERROR,和UART 什么什么鬼,然后打印

insert USB啥的。

6 分析原因

打印的第一个SD CHECKSUM ERROR可以理解,因为那是SD0通道的iNAND校验失败,第二个是什么

情况勒,我们发现这时SD2通道也校验失败,并从UART启动也失败,再从USB启动,没有插USB。那

为什么校验失败了呢,sd_fusing脚本给BL1添加16字节的头部啊,程序是对的啊。这是因为我们的

uboot.bin的前16字节被覆盖了,我们start.S的头16字节是在设置中断向量表,所以在脚本给uboot.bin

添加头的时候给覆盖掉了,所以我们应该在start.S的开始处添加16字节占个位置,来存放头信息。

// uboot头部16字节的用于填充校验和 不加会check sum error
.word 0x2000
.word 0x0
.word 0x0
.word 0x0

再次编译烧录,我们可以看到这是算是运行了,现在只打印了一个SD CHECKSUM ERROR。下面运行到

哪里失败了呢,我们再分析,不过算是一件可以运行了!

接下来,基本的温饱问题解决了,我们要做的就是,分析源码,修改源码,编译源码,分析运行效果···

阻碍1:

好了,现在只能从start.S开始分析了,我们可以写个点亮某个LED的代码段来调试程序死在哪了。我们

发现,uboot第一阶段运行都正常,就是说在调用lowlevel_init之前都可以工作,但是在跳转到lowlevel_init

的第一句就不行了,这是为嘛呢?经过分析发现,在uboot的第一阶段(只有8KB,在SRAM运行),我们

没有把lowlevel_init链接到前8K,所以跳转不过去。

解决1:

既然不能链接到前8KB,那么就去修改链接脚本把lowlevel_init.o链接到前面来,在arch/arm/cpu/打开

u-boot.lds,修改如下

.text :
{
*(.__image_copy_start)
CPUDIR/start.o (.text*)
/* 把lowlevel_init.o链接到前8KB */
board/samsung/goni/lowlevel_init.o (.text*)

*(.text*)
}

重新编译,发现lowlevel_init函数重定义了,分析可知lowlevel_init.o链接了2次,我们只要删了一次就好,

打开board/samsung/goni/Makefile,修改如下

# 新添加的
#SOBJS := lowlevel_init.o
LOW := lowlevel_init.o

SRCS    := $(SOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS-y))
SOBJS := $(addprefix $(obj),$(SOBJS))
# 新添加的
all: $(obj).depend $(LOW) $(LIB)

$(LIB): $(obj).depend $(SOBJS) $(OBJS)
$(call cmd_link_o_target, $(SOBJS) $(OBJS))

OK,编译烧录,这时我们就可以运行到lowlevel_init这边了,我们在这里添加开发板供电置锁功能和点亮

一个LED,和让串口打印字符'O',修改如下

<span style="white-space:pre"></span>/* Disable Watchdog */
ldreqr0, =S5PC100_WATCHDOG_BASE@ 0xEA200000
ldrner0, =S5PC110_WATCHDOG_BASE@ 0xE2700000
strr5, [r0]

<span style="color:#3333ff;">/* 给开发板供电锁存 */
ldr r0, =0xE010E81C
ldr r1, =0x301
str r1, [r0]

/* 亮一个灯 */
ldr r0, =0x11111111
ldr r1, =0xE0200240
str r0, [r1]
ldr r0, =((1<<3) | (0<<4) | (1<<5))
ldr r1, =0xE0200244
str r0, [r1]<span style="white-space:pre"></span>//我们添加的</span>
在uart_asm_init函数 末尾添加

<span style="color:#3333ff;">// 这里我们直接向uart2的发送缓冲区写数据就会发送 因为在IROM中已经初始化了UART
ldrr1, =0x4f4f4f4f
ldr r0, =0xE2900820 // UTXH2
strr1, [r0]// 'O'</span>
好了,到这里编译烧录,你会发现 开发板置锁了,LED亮了,串口打印了字符'O'


阻碍2与解决2:

串口初始化完了,接下来的任务就是DDR的初始化,然后完成重定位,长跳转到DDR去运行,那么问题来

了,怎么初始化DDR。说难也难,说简单也简单,我们只要把我们之前裸机的DDR初始化代码COPY过来

即可,我们把他copy到board/samsung/goni目录下,然后进行把这个文件链接到前8KB即可,方法同上面

接着,我们在lowlevel_init.S的串口初始化后面添加DDR的初始化,如下所示:

/* for UART */
bluart_asm_init

/* 初始化DDR 自行添加移植 */
bl mem_ctrl_asm_init

/* DDR初始化成功打印字符'K' */
ldrr1, =0x4b4b4b4b
ldr r0, =0xE2900820 // UTXH2
strr1, [r0]// 'K'

movlr, r11// 我们自己让他强制返回
movpc, lr
初始化完成后打印'K'。并且返回。

阻碍3与解决3:

DDR初始化完了,接着就要进行代码重定位,并长跳转到DDR中运行,辣么,这么重定位勒,我们是从SD

卡启动的,并且分析sd_fusing目录下的烧录脚本可知,我们把uboot加16字节的头信息烧到了SD卡的第一

个扇区开始处,并且把整个uboot镜像烧录到49扇区开始的位置。所以我们重定位的方向是从SD卡的第49

扇区开始拷贝uboot.bin大小到DDR的约定位置。我们之前在裸机代码中已经写过了代码的重定位,所以跟

上面的方法类似,拷贝文件过来修改即可,我们一样拷贝到了/board/samsung/goni目录下,并且修改

Makefile和u-boot.lds链接到前8KB即可,修改如下(/board/samsung/goni/myRelocate.c):

#define SD_START_BLOCK49
#define SD_BLOCK_CNT(512*2) // 512KB
#define DDR_START_ADDRCONFIG_SYS_TEXT_BASE //0x34800000
// 通道号:0,或者2
// 开始扇区号:49
// 读取扇区个数:512*2
// 读取后放入内存地址:0x34800000
typedef unsigned int bool;
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);

// 从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000,然后跳转到23E00000去执行
void relocate_2_ddr(void)
{
// 读取SD卡扇区到DDR中
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)(*(unsigned int *)0xD0037F98);
p1(2, SD_START_BLOCK, SD_BLOCK_CNT, (unsigned int *)DDR_START_ADDR, 0);
}
这里我们从49扇区拷贝512KB到DDR的0x34800000。接下来就是清BSS段,并且长跳转,如下:

// 重定位
bl relocate_2_ddr
// 清BSS
clear_bss:
ldrr0, _bss_start/* find start of bss segment */
ldrr1, _bss_end/* stop here */
mov r2, #0x00000000<span style="white-space:pre"></span>/* clear */
clbss_l:
strr2, [r0]/* clear loop... */
addr0, r0, #4
cmpr0, r1
bleclbss_l
ldr pc, __main// 长跳转
__main:
.word_main
接下来我们就运行到了DDR中。在DDR中首先设置栈,然后运行board_init_f函数

阻碍4与解决4:

接下来就是进行一系列的初始化了,哪里不对改哪里,首先,我们可以修改板子的名字,这个简单

不赘述,重头戏是时钟,我们看到可串口上打印的是400MHz,那是因为我们之前没有设置过时钟

,使用的是IROM中初始化的,默认的初始化的ARMCLK是400MHz,我们要把它初始化为1GHz,

怎么做勒,一样的,我们在裸机代码中写过时钟的初始化,SO,在/board/samsung/goni目录下添

加时钟的初始化文件myClockInit.S把时钟初始化为1GHz即可,并且把它放到lowlevel_init.S中去,

时钟初始化代码较长,这里就不贴了,在下面的地方调用初始化之~

/* Disable Watchdog */
ldreqr0, =S5PC100_WATCHDOG_BASE@ 0xEA200000
ldrner0, =S5PC110_WATCHDOG_BASE@ 0xE2700000
strr5, [r0]

/* 时钟初始化 */
bl my_clock_init
在关看门狗后,初始化时钟。这时我们会看到时钟变成了1000MHz。接下来就是配置DDR的信息

DDR几个bank,每个bank有多大这些···,因为我们的板子DDR总共512MB,bank0@256MB,

bank1@256MB,其实地址分别为0x30000000和0x40000000所以其配置,如下所示:

/* Goni has 3 banks of DRAM, but swap the bank */
#define CONFIG_NR_DRAM_BANKS2
#define PHYS_SDRAM_1CONFIG_SYS_SDRAM_BASE/* OneDRAM Bank #0 */
#define PHYS_SDRAM_1_SIZE0x10000000/* 256 MB in Bank #0 */
#define PHYS_SDRAM_20x40000000/* mDDR DMC1 Bank #1 */
#define PHYS_SDRAM_2_SIZE0x10000000/* 256 MB in Bank #1 */
//#define PHYS_SDRAM_30x50000000/* mDDR DMC2 Bank #2 */
//#define PHYS_SDRAM_3_SIZE0x0/* 128 MB in Bank #2 */
接下来配置gd->bd->bi_arch_number要与内核移植,所以我们配置为MACH_TYPE_SMDKV210

具体值为2456。