如前文U-boot-2009.03移植之四中所讲,第一阶段,也就是支持2440的阶段,首先要做的事情就是修改/cpu/arm920t/start.s
start.s 源码注解参考
http://blog.chinaunix.net/u1/34474/showart.php?id=2217073
如何添加支持2440修改参考
http://zjbintsystem.blog.51cto.com/964211/211345
(针对UBOOT从Nand启动的支持部分,参考这个网页修改,实际上这个网页给出的移植攻略很全,可以多看。)
值得注意的是,在以前版本的Uboot都是从nor启动,所以在拷贝代码那部分的源码跟我们贴出来的这个修改后的源码不同,原因就是那些支持从nor启动的代码已经被删除,取而代之的是我们加上了从nand启动的代码
http://blog.csdn.net/Charistain_huang/archive/2010/04/09/5469007.aspx
(针对u-boot-2009,但是有不少错误,尤其是对于start.s的解释,更多要结合下面这一篇看)
http://home.eeworld.com.cn/my/space.php?uid=135723&do=blog&id=25347
(针对u-boot-1.3.4,但是个人认为是写的最好最全的一篇,与2009非常相近)
需要注意的是,两片文章在“代码搬移”这部分代码的区别,个人认为1.3.4的是正确的。
我在这里只附上修改后的源码并做简要解释,源码如下:
注释部分由“//hadise=========”开头
#include <config.h>
#include <version.h>
//hadise===========#include <status_led.h> 已经被删除掉 /*这是针对AT91RM9200DK开发板的,删掉。*/
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
.globl _start
_start: b start_code
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: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
//hadise=========以上几句是装载特定的地址到PC寄存器,当发生特殊的事件时候,程序会跳转到这几句话中的某一句,装载
//地址到PC中,实际上就是跳转到相应的地址指向特定的代码
.balignl 16,0xdeadbeef //hadise=====4字节对齐,0xdeadbeef是magic码
/*
*************************************************************************
*
* Startup Code (called from the ARM reset exception vector)
*
* do important initonly if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/*
* the actual start code
*/
start_code:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr //hadise复制cpsr 的内容到r0中
bic r0,r0,#0x1f //hadise==bic是位清除指令,这句话的意思是,让r0与立即数#0x1f相与,返回r0。实质是取低5位
orr r0,r0,#0xd3 //hadise==因为低5位的值代表了ARM状态,其中,d3这个值,代表了svc32模式
msr cpsr,r0 //hadise==然后把设置好的状态,写回到cpsr
//hadise=======以上4句话,是要设置ARM状态为SVC32模式,这里说点小知识:
ARM6开始有了32位模式,32 位的与 26 位的不同是:
PC (R15)是完全的 32 位宽,并只用做程序计数器。
PSR 包含在它自己的寄存器 CPSR(当前程序状态寄存器) 中。
每个有特权的模式都有一个专有的 SPSR 寄存器,用来保存 CPSR。
这里有两个新的特权模式,每个有特权的模式都有 R13 和 R14 的专有复件。
这就造成对于R15的操作,也就是对于PC的操作,不能用以前的指令,要用专用的指令 mrs 和 msr
@bl coloured_LED_init
@bl red_LED_on
//hadise========以上两句是针对AT91RM9200DK开发板的,因此删除
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
/*
* relocate exception table
*/
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif
//hadise=========针对AT91RM9200DK开发板的代码
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
/* turn off the watchdog */
# if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#else
# define pWTCON 0x53000000
# define INTMOD 0x4A000004
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
# endif
//hadise========以上这里有自己修改,针对2440和2410的不同,设置了相应寄存器的地址
#define CLK_CTL_BASE 0x4C000000
#define MDIV_405 0x7f<<12
#define PSDIV_405 0x21
#define MDIV_200 0xa1<<12
#define PSDIV_200 0x31
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
//hadise====以上3句关闭看门狗
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
//hadise=====以上3句关闭所有中断
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
# if defined(CONFIG_S3C2440)
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
@ldr r0, =CLKDIVN
@mov r1, #3
@str r1, [r0]
//hadise=====以上屏蔽了时钟的修改,我们在以后的函数中再修改
# if defined(CONFIG_S3C2440) /* FCLK:HCLK:PCLK = 1:4:8 */
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0 /*read ctrl register */
orr r1, r1, #0xc0000000 /*Asynchronous */
mcr p15, 0, r1, c1, c0, 0 /*write ctrl register */ /*now, CPU clock is 405.00 Mhz */
mov r1, #CLK_CTL_BASE /* */
mov r2, #MDIV_405 /* mpll_405mhz */
add r2, r2, #PSDIV_405 /* mpll_405mhz */
str r2, [r1, #0x04] /* MPLLCON */
//hadise======================================以上设置时钟的代码很重要,很多时候UBOOT移植不成功就是时钟没设置对
#else /* FCLK:HCLK:PCLK = 1:2:4 */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0 /*read ctrl register */
orr r1, r1, #0xc0000000 /*Asynchronous */
mcr p15, 0, r1, c1, c0, 0 /*write ctrl register */ /*now, CPU clock is 202.8 Mhz */
mov r1, #CLK_CTL_BASE /* */
mov r2, #MDIV_200 /* mpll_200mhz */
add r2, r2, #PSDIV_200 /* mpll_200mhz */
str r2, [r1, #0x04] # endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 || defined(CONFIG_S3C2440)*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
adr r0,_start
ldr r1,_TEXT_BASE
cmp r0,r1
blne cpu_init_crit
#endif
//hadise===============比较_start和_TEXT_BASE,以判断当前UBOOT是在flash中运行还是内存中运行,如果不等于,表示在
//hadise===============flash中运行,那么,跳转到cpu_init_crit
//hadise=====由board/samsung/mini2440/u-boot.lds链接文件中可以看出,testbase就是start.o开始的地方,如果是在flash中,
//haidise===则应该等于0,否则等于内存起始物理地址,对于mini2440来说,应该是0x30000000,而start则绝对的是0x0000000.
//hadise====跳入cpu_init_crit ,这是一个系统初始化函数,他还会调用board/*/lowlevel_init.S中的lowlevel_init函数。
主要是对系统总线的初始化,初始化了连接存储器的位宽、速度、刷新率等重要参数。经过这个函数的正确初始化,Nor Flash、SDRAM才可以被系统使用。下面的代码重定向就依
赖它。
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
@#ifdef CONFIG_S3C2440_NAND_BOOT
#define NAND_CTL_BASE 0x4E000000/* Offset */
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20
#define LENGTH_UBOOT 0x40000
@ reset NAND
/*往下四段内容都是针对S3C2440的关于NAND-FLASH的寄存器的设置,具体有什么作用,看了datasheet,有些明白有些不明白*/
mov r1, #NAND_CTL_BASE
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
str r2, [r1, #oNFCONF] /*这些宏是在include/configs/qljt2440.h中被定义的*/
ldr r2, [r1, #oNFCONF] /*还是弄不懂为什么上面一句str以后还要有这句的ldr命令?why?难道是多余的?*/
ldr r2, =( (1<<4)|(0<<1)|(1<<0) ) @ Active low CE Control
str r2, [r1, #oNFCONT]
ldr r2, [r1, #oNFCONT]
ldr r2, =(0x6) @ RnB Clear
str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
/*delay一段时间*/
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
/*等待nand-flash的复位完毕信号*/
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x4
beq nand2
ldr r2, [r1, #oNFCONT]
orr r2, r2, #0x2 @ Flash Memory Chip Disable /*在这里先Display fansh CE先,在C函数中对falsh进行*/
str r2, [r1, #oNFCONT] /*操作的时候才enable,为什么这样操作不太清楚*/
/*下面这段用来初始化栈指针sp和帧指针fp,至于它们的定义和作用参考文件夹” 栈指针sp和帧指针fp”里面的内容
记住它们都是与函数调用时候相关的。简单来讲就是子函数被调用以后是通过指针的相对位置来查找调用参数和局部变量的,但是由于sp经常变化,所以需要fp来协助。*/
@ get ready to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer /*sp 是指堆栈指针*/
mov fp, #0 @ no previous frame, so fp=0
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
adr r0,_start
ldr r1,_TEXT_BASE
cmp r0,r1
beq stack_setup
@@@@@@@@@@@@@@@@@@@@@@@@@@@
//这部分做了特殊记号,是因为当初吃了亏,忘记了加这部分。参考网中提到的攻略里面没有写到这一部分,但是一定要加上这几句话。
作用是,如果不是在ram中运行,则要先跳到初始化堆栈的代码部分,在stack_setup代码段中,完成堆栈初始化,然后调用_start_armboot进入第二阶段C语言部分,如果不加这句话,则会继续把代码从nand搬运到ram中,不必要!
@ copy U-Boot to RAM /*vivi里面应该是有一段是针对gpio的程序,也许使用来debug用的信号灯,这里省略了*/
/* TEXT_BASE 是uboot自己的入口地址,在u-boot-1.3.4-board/qljt/qljt2440的config.mk中定义
有趣的是外国人的逆向思维很厉害,它们很灵活地把它放在SDRAM的最后0x80000地方,也就是0x33F80000
*/
ldr r0, =TEXT_BASE /*r0 : 把u-boot复制到ram的那个位置*/
mov r1, #0x0 /*r1 : 从falsh的那个位置开始复制*/
mov r2, #0x20000 /*r2 : 复制多大的内容*/
bl nand_read_ll /*跳到执行uboot复制的程序入口,这个函数从哪里来?也是来自vivi的,没办法*/
tst r0, #0x0 /*这里特别注意r0的值是指nand_read_ll 执行完以后的返回值,而不是上面
ldr r0, =TEXT_BASE 的值,初学者往往在这里想不通*/
beq ok_nand_read
bad_nand_read: /*如果读nand_read失败的话,那么sorry,重来,或者检查硬件*/
loop2: b loop2 @ infinite loop
ok_nand_read:
@ verify
/*计算机就是好,很容易就可以检测我们放在SDRAM中的u-boot是不是flash中的uboot。
本开发板使用的是nand-falsh的启动方式,板子一上电并不是马上进入SDRAM执行程序的。是这样的:板子一上电,S3C2440自动把nand-falsh中从0地址开始的4Kbytes复制到
S3C2440集成的某个缓冲区里面(起始地址是0x00),从那里开始执行,那4K程序负责把整个uboot复制到SDRAM,然后才跳到SDRAM开始正真的UBOOT(这个技术是有个专业名字叫做
stepingstone),*/
/*下面这段程序的作用就是用开始执行的4Kbytes程序跟我们复制到SDRAM中的uboot的前4K程序进行比较,从而校验*/
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq stack_setup
bne go_next
notmatch:
loop3: b loop3 @ infinite loop
//=========================================================
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
_start_armboot: .word start_armboot
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
mov ip, lr
#if defined(CONFIG_AT91RM9200EK)
#else
bl lowlevel_init
//hadise=========================这一句很重要,跳转到相应板子的lowlevel_init.S,注意看上面的英文解释
#endif
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/*
*************************************************************************
*
* Interrupt handling
*
*************************************************************************
*/
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE)
sub r2, r2, #(CONFIG_SYS_MALLOC_LEN)
sub r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r7, sp, #S_PC
stmdb r7, {sp, lr}^ @ Calling SP, LR
str lr, [r7, #0] @ Save calling PC
mrs r6, spsr
str r6, [r7, #4] @ Save CPSR
str r0, [r7, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE)
sub r13, r13, #(CONFIG_SYS_MALLOC_LEN)
sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif