此部分为Uboot第一阶段,后期陆续补上第二阶段。
在这部分还和开发板没关系,只和处理器有关
.text
.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
_undefined_instruction: .word undefined_instruction
_software_interrupt: .wordsoftware_interrupt
_prefetch_abort: .wordprefetch_abort
_data_abort: .worddata_abort
_not_used: .wordnot_used
_irq: .wordirq
_fiq: .wordfiq
undefined_instruction:
nop
software_interrupt:
nop
prefetch_abort:
nop
data_abort:
nop
not_used:
nop
irq:
nop
fiq:
nop
reset:
nop
OUTPUT_ARCH(arm)
ENTRY(_start) 入口
SECTIONS {
. = 0x30008000; 设置起始位置
. = ALIGN(4); 4字节对齐
.text : 代码段
{
start.o (.text) 放start.o文件以及其他文件的代码段
*(.text)
}
. = ALIGN(4);
.data :
{
*(.data)
}
. = ALIGN(4);
bss_start = .; @记录bss起始位置
.bss :
{
*(.bss)
}
bss_end = .; @记录bss结束位置
}
all: start.o
arm-linux-ld -Tuboot.lds-o uboot.elf $^
arm-linux-objcopy -Obinary uboot.elf uboot.bin
%.o : %.S
arm-linux-gcc -g -c $^
%.o : %.c
arm-linux-gcc -g -c $^
.PHONY: clean
clean:
rm *.o *.elf *.bin
将start.S中reset改写成
reset:
bl set_svc
set_svc:
mrs r0, cpsr
bic r0, r0,#0x1f @后5位清零
orr r0,#0xd3 @d3屏蔽了irq,frq
msr cpsr, r0
将start.S中reset改写成
reset:
bl set_svc
bldisable_watchdog
set_svc: @6440210 y也一样
mrs r0, cpsr
bic r0, r0,#0x1f @后5位清零
orr r0,#0xd3 @d3屏蔽了irq,fiq
msr cpsr, r0
#define pWTCON 0x530000000 @看门狗时钟控制寄存器地址6440 210不同
disable_watchdog:
ldr r0, = pWTCON
mov r1, #0x0
str r1, [r0]
将start.S中reset改写成
reset:
bl set_svc
bl disable_watchdog
bldisable_interrupt
set_svc://6440 210 y @也一样
mrs r0, cpsr
bic r0, r0,#0x1f @后5位清零
orr r0,#0xd3 @d3屏蔽了irq,fiq
msr cpsr, r0
#define pWTCON 0x530000000 @看门狗时钟控制寄存器地址6440 210不同
disable_watchdog:
ldr r0, = pWTCON
mov r1, #0x0
str r1, [r0]
disable_interrupt: @2440
mvn r1, #0x0 @所有寄存器 将0x0取反
ldr r0,=0x40000008 @INTIMSK寄存器地址
str r1, [r0]
6410和210的话改下寄存器地址
disable_interrupt: @6410
mvn r1, #0x0
ldr r0,=0x71200014 @两个中断使能寄存器地址
str r1, [r0]
ldr r0, =0x71300014
str r1, [r0]
disable_interrupt: @210
mvn r1, #0x0
ldr r0, =0xf2000014 @四个中断使能清除寄存器地址
str r1, [r0]
ldr r0, =0xf2100014
str r1, [r0]
ldr r0, =0xf2200014
str r1, [r0]
ldr r0, =0xf2300014
str r1, [r0]
将start.S中reset改写成
reset:
bl set_svc
bldisable_watchdog
bldisable_interrupt
bl disable_mmu
set_svc: @6440 210 y也一样
mrs r0, cpsr
bic r0, r0,#0x1f @后5位清零
orr r0,#0xd3 @d3屏蔽了irq,fiq
msr cpsr, r0
mov pc, lr @返回
#define pWTCON 0x530000000 @看门狗时钟控制寄存器地址6440 210不同
disable_watchdog:
ldr r0, = pWTCON
mov r1, #0x0
str r1, [r0]
mov pc,lr @返回
disable_interrupt: @2440
mvn r1, #0x0
ldr r0,=0x40000008 @INTIMSK寄存器地址
str r1, [r0]
mov pc,lr @返回
disable_mmu: @2440、6420、210一样
mcr p15, 0,r0, c7, c7, 0 @使Icache和Dcache失效
mrc p15, 0,r0. c1, c0, 0 @读控制寄存器
bic r0, r0,#0x00000007 @第0/1/2位全变0
mcr p15, 0,r0, c1, c0, 0 @写控制寄存器
mov pc,lr @返回
这里的bl是跳转的意思,lr(r14)保存了返回地址,pc(r15)是当前地址,把lr给pc就是从子程序返回。Lr(r14)的一般有两个作用:
(1)当使用bl或blx跳转到子程序的时候,r14保存了返回地址,可以在调用过程结尾恢复。
(2)异常中断发生时,这个异常模式特定的物理r14被设置成该异常模式将要返回的地址。
另外注意pc,在调试时显示的当前指令地址,而用mov lr, pc的时候lr保存的是此指令向后数两条指令的地址,大家可以试一下用mov pc, pc ,结果得到的是跳转两条指令,这个原因是由于arm的流水线造成的,预取两条指令的结果。
1. 设置GPIO控制寄存器,把引脚设置为输出功能(#define GPBCON 0x56000010)
2. 根据原理图设置GPIO数据寄存器,点亮LED(#define GPBDAT 0x56000014)
reset:
bl light_led
#define GPBCON 0x56000010 //控制寄存器地址
#define GPBDAT 0x56000014 @数据寄存器地址
light_led:
ldr r0, =GPBCON
ldr r1,=0x15400 @用的是5、6、7、8引脚 0101010100000000
str r1, [r0]
ldr r0, =GPBDAT
ldr r1,=0x6BF //11010111111 @用的是5、6、7、8引脚
str r1, [r0]
mov pc, lr
1. 配置lock time
2. 设置分频系数(Uboot 采用1:4:8)
3. 设置cpu异步模式
4. 设置FCLK
reset:
bl init_clock
#define CLKDIVN 0x4c000014
#define MPLLCON 0x4c000004
#define MPLL_405MHZ((127<<12)|(2<<4)|(1<<0))
init_clock:
ldr r0, =CLKDIVN @设置分频系数
mov r1, #0x5 HDIVN 2(10) PDIVN 1(1) 1:4:8
str r1, [r0]
mrc p15,0,r0,c1,c0,0 @设置cpu异步模式
orr r0,r0,#0xc0000000 1100 0000 0000
mcr p15,0,r0,c1,c0,0
ldr r0, =MPLLCON @设置FCLK
ldr r1, =MPLL_405MHZ
str r1, [r0]
mov pc, lr
reset:
bl init_sdram
#define mem_contrl 0x48000000 @13三个寄存器第一个寄存器地址
init_sdram:
ldr r0, =mem_contrl @初始地址
add r3, r0, #4*13 @结束地址,初始地址加上4个字节,13个寄存器
adrl r1, mem_data @r1的指针指向基地址,13个寄存器设置参数
0: @循环记入13个寄存器设置参数
ldr r2, [r1], #4
str r2, [r0], #4
cmp r0, r3
bne 0b @往前跳转到0
mov pc, lr
mem_data: @13个寄存器设置
.long 0x22000000
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00018001
.long 0x00018001
.long 0x008c04f5
.long 0x000000b1
.long 0x00000030
.long 0x00000030
下面是6410内存初始化,和2440有很大的区别
图中是6410 DRAM 控制器初始化流程 第3步多余的
新建mem.S,这里新建这个文件主要是实现内存初始化,其实在start.S添加也可以,只是浏览不方便。注意要在start.S中reset添加bl mem_init,Makefile中将all: start.o改为all: start.o mem.o
.text @修改在代码段
.global mem_init @设mem_init为全局
mem_init:
ldr r0,=0x7e00f120
mov r1, #0x8
str r1, [r0]
ldr r0,=0x7e001004 @内存控制命令寄存器
mov r1, #0x4 @根据手册得知需要先进入配置模式
str r1, [r0]
ldr r0,=0x7e001010 @刷新寄存器地址
ldr r1, =( ( 7800/ ( 1000000000/133000000 ) + 1 ) ) @设置刷新时间
str r1, [r0]
ldr r0,=0x7e001014 @CAS latency寄存器
mov r1, #(3<< 1)
str r1, [r0]
ldr r0,=0x7e001018 @t_DQSS寄存器
mov r1, #0x1
str r1, [r0]
ldr r0,=0x7e00101c @T_MRD寄存器
mov r1, #0x2
str r1, [r0]
ldr r0,=0x7e001020 @t_RAS寄存器
ldr r1, =( ( 45 /( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0,=0x7e001024 @t_RC寄存器
ldr r1, =( ( 68 /( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0,=0x7e001028 @t_RCD寄存器
ldr r1, =( ( 23 /( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0,=0x7e00102c @t_RFC寄存器
ldr r1, =( ( 80 /( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0,=0x7e001030 @t_RP寄存器
ldr r1, =( ( 23 /( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0,=0x7e001034 @t_rrd寄存器
ldr r1, =( ( 15 /( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0,=0x7e001038 @t_wr寄存器
ldr r1, =( ( 15 /( 1000000000 / 133000000 ) + 1 ) )
@ ldr r2, [r0]
str r1, [r0]
ldr r0,=0x7e00103c @t_wtr寄存器
mov r1, #0x07
str r1, [r0]
ldr r0, =0x7e001040 @t_xp寄存器
mov r1, #0x02
str r1, [r0]
ldr r0,=0x7e001044 @t_xsr寄存器
ldr r1, =( ( 120/ ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0,=0x7e001048 @t_esr寄存器
ldr r1, =( ( 120/ ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0,=0x7e00100c @内存控制配置寄存器
ldr r1,=0x00010012 @配置控制器
str r1, [r0]
ldr r0,=0x7e00104c @32位DRAM配置控制寄存器
ldr r1, =0x0b45
str r1, [r0]
ldr r0,=0x7e001200 @片选寄存器
ldr r1, =0x150f8
str r1, [r0]
ldr r0,=0x7e001304 @用户配置寄存器
mov r1, #0x0
str r1, [r0]
@issue NOP
ldr r0,=0x7e001008
ldr r1,=0x000c0000
str r1, [r0]
ldr r1,=0x00000000
str r1, [r0]
ldr r1,=0x00040000
str r1, [r0]
ldr r1, =0x000a0000
str r1, [r0]
ldr r1,=0x00080032
str r1, [r0]
ldr r0,=0x7e001004
mov r1, #0x0
str r1, [r0]
check_dmc1_ready:
ldr r0,=0x7e001000
ldr r1, [r0]
mov r2, #0x3
and r1, r1, r2
cmp r1, #0x1
bne check_dmc1_ready
nop
mov pc, lr
在reset:添加
bl copy_to_ram
copy_to_ram:
ldrr0, =0x0 @2440起点地址
ldrr1, =0x30008000 @终点地址
addr3, r0, #1024*4
copy_loop:
ldrr2, [r0], #4 @读下一内存
strr2, [r1], #4 @写下一内存
cmpr0, r3 @是否到了尾部
bnecopy_loop
movpc, lr
这部分2440、6410、210只在地点地址和终点地址不同具体如下
2440 起点 0x0 终点 30008000
6410 0x0c000000 0x50008000
210 0xd0020000 0x20008000
Ldr pc,=reset <==> mov pc, 0x3008058 (反汇编reset地址是0x3008058)
注意:考虑到的垫脚石,210的起点地址要设置为0xd0020010(0xd0020000+16)
1.堆栈的初始化
根据SP栈可分为满栈和空栈。满栈:当堆栈指针SP总是指向最后一个如堆栈的数据.ARM采用满栈。空栈:当堆栈指针SP总是指向下一个将要放入数据的空位置。
根据SP移动方向分为升栈和降栈。升栈:SP从低地址è 高地址。ARM采用升栈。降栈:SP从高地址è低地址。
reset:
bl init_stack
init_stack:
ldr sp, =0x34000000 @sp指向从起始地址到64M的地方
mov pc ,lr
sp设置6410 :0x54000000 210:0x24000000
2.清除bss段(bss:未初始化的全局变量)[sp,#-4] <==> sp=sp-4
clean_bss:
ldr r0, =bss_start
ldr r1, =bss_end
cmp r0, r1
moveq pc, lr @如果相同,直接返回
clean_loop:
mov r2, #0
str r2, [r0], #4
cmp r0, r1
bne clean_loop @还不相等,继续
mov pc, lr @直到移到bss段,返回
第二阶段
将在reset中添加ldr pc, = uboot_main,删除bl light_led,
将Makefile中all: start.o mem.o 改为all: start.o mem.o main.o
新建C代码main.c
#define GPBCON (volatile unsigned long*)0x56000010 //控制寄存器地址
#define GPBDAT (volatile unsigned long*)0x56000014 //数据寄存器地址
int uboot_main()
{
*(GPBCON) =0x15400; @用的是5、6、7、8引脚 0101010100000000
*(GPBDAT) =0x6BF; @11010111111 用的是5、6、7、8引脚
return 0;
}
在汇编程序中light_led设这样的:
reset:
bl light_led
#define GPBCON 0x56000010 @控制寄存器地址
#define GPBDAT 0x56000014 @数据寄存器地址
light_led:
ldr r0, =GPBCON
ldr r1,=0x15400 @用的是5、6、7、8引脚 0101010100000000
str r1, [r0]
ldr r0, =GPBDAT
ldr r1,=0x6BF @11010111111 用的是5、6、7、8引脚
str r1, [r0]
mov pc, lr
用内联函数汇编实现上面代码,可以这样做:将在reset中添加ldr pc, =uboot_main,删除bllight_led,
将Makefile中all: start.o mem.o 改为all: start.o mem.o main.o
新建C代码main.c
#define GPBCON 0x7f008800
#define GPBDAT 0x7f008808
int main()
{
__asm__(
“ldr r1,=0x11110000\n” @汇编语句部分
“str r1,[%0]\n” @汇编语句部分
“ldr r1,=0xa0\n” @汇编语句部分
“str r1,[%1]\n” @汇编语句部分
: @输出部分
:”r”(GPBCON),“r”(GPBDAT) @输入部分
:”r1” @破坏描述部分
);
return 0;
}
有个问题:如何判断是否从norflash启动?如果从norflash启动的话,0地址对应于norflash,但是norflash不能像内存一样去写;如果从nandflash启动的话0地址对应于片内内存sram,0地址是可以写的,判断思路就是:把零地址读出来,然后往0地址写一个特定的数,判断0地址等不等于写进去的特定数,即可。