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 */在uart_asm_init函数 末尾添加
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>
<span style="color:#3333ff;">// 这里我们直接向uart2的发送缓冲区写数据就会发送 因为在IROM中已经初始化了UART好了,到这里编译烧录,你会发现 开发板置锁了,LED亮了,串口打印了字符'O'。
ldrr1, =0x4f4f4f4f
ldr r0, =0xE2900820 // UTXH2
strr1, [r0]// 'O'</span>
阻碍2与解决2:
串口初始化完了,接下来的任务就是DDR的初始化,然后完成重定位,长跳转到DDR去运行,那么问题来
了,怎么初始化DDR。说难也难,说简单也简单,我们只要把我们之前裸机的DDR初始化代码COPY过来
即可,我们把他copy到board/samsung/goni目录下,然后进行把这个文件链接到前8KB即可,方法同上面
接着,我们在lowlevel_init.S的串口初始化后面添加DDR的初始化,如下所示:
/* for UART */初始化完成后打印'K'。并且返回。
bluart_asm_init
/* 初始化DDR 自行添加移植 */
bl mem_ctrl_asm_init
/* DDR初始化成功打印字符'K' */
ldrr1, =0x4b4b4b4b
ldr r0, =0xE2900820 // UTXH2
strr1, [r0]// 'K'
movlr, r11// 我们自己让他强制返回
movpc, lr
阻碍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这里我们从49扇区拷贝512KB到DDR的0x34800000。接下来就是清BSS段,并且长跳转,如下:
#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);
}
// 重定位接下来我们就运行到了DDR中。在DDR中首先设置栈,然后运行board_init_f函数
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
阻碍4与解决4:
接下来就是进行一系列的初始化了,哪里不对改哪里,首先,我们可以修改板子的名字,这个简单
不赘述,重头戏是时钟,我们看到可串口上打印的是400MHz,那是因为我们之前没有设置过时钟
,使用的是IROM中初始化的,默认的初始化的ARMCLK是400MHz,我们要把它初始化为1GHz,
怎么做勒,一样的,我们在裸机代码中写过时钟的初始化,SO,在/board/samsung/goni目录下添
加时钟的初始化文件myClockInit.S把时钟初始化为1GHz即可,并且把它放到lowlevel_init.S中去,
时钟初始化代码较长,这里就不贴了,在下面的地方调用初始化之~
/* Disable Watchdog */在关看门狗后,初始化时钟。这时我们会看到时钟变成了1000MHz。接下来就是配置DDR的信息
ldreqr0, =S5PC100_WATCHDOG_BASE@ 0xEA200000
ldrner0, =S5PC110_WATCHDOG_BASE@ 0xE2700000
strr5, [r0]
/* 时钟初始化 */
bl my_clock_init
DDR几个bank,每个bank有多大这些···,因为我们的板子DDR总共512MB,bank0@256MB,
bank1@256MB,其实地址分别为0x30000000和0x40000000所以其配置,如下所示:
/* Goni has 3 banks of DRAM, but swap the bank */接下来配置gd->bd->bi_arch_number要与内核移植,所以我们配置为MACH_TYPE_SMDKV210
#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 */
具体值为2456。