本次移植u-boot-2010.09是基于S3C2440的FL440板子,板子自带NANDFLASH而没有NORFLASH,所以在U-BOOT启动的过程中必须实现从NANDFLASH到SDRAM的重定向。
其中最重要的就是在U-BOOT开始的start.S汇编代码,这段代码要完成工作:
1,异常中断向量表,复位后异常向量处理
2, 跳转到代码实际执行处start_code
3,关闭看门狗WATCHDOG
3,关闭所有中断INTERRUPT
4,设置时钟分频,主要设置寄存器CLKDVN,MPLLCON,UPLLCON
5,关闭MMU和CACHE,并调用lowlevel_init.S完成SDRAM和NANDFLASH的初始化,为代码的重定向做准备
6,设置堆栈,并且跳入第二阶段的C代码
7,异常向量处理代码
以下为start.S的分析:
1,异常中断向量表,复位后异常向量处理
//声明一个全局标量,在cpu/arm920t/u-boot.lds中有定义,即代码的入口地址,也是编译地址
_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 //.word 定义一个32位的地址标识
_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
.balignl 16,0xdeadbeef//将地址偏移为16的整数倍,空余的内容填上0xdeadbeef,这个数即“Magic Number”,可用判断当前u-boot执行位置
_TEXT_BASE:
.word TEXT_BASE //在config.mk中有定义,即u-boot自启时flash从定向到sdram的地址
.globl _armboot_start //声明一个全局变量,之后要调用
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start //连接脚本u-boot.lds中有定义
_bss_start:
.word __bss_start
.globl _bss_end //连接脚本u-boot.lds中有定义
_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
2, 跳转到代码实际执行处(设置管理模式=>关闭看门狗=>关闭所有中断=>设置时钟分频)
/*
* the actual start code
*/
start_code:
/*
* set the cpu to SVC32 mode
*/
//设置管理模式 31 30 29 28 7 6 4 3 2 1 0
mrs r0, cpsr // CPSR N Z C V I F M4 M3 M2 M1 M0
bic r0, r0, #0x1f // 1 0 0 1 1
orr r0, r0, #0xd3
msr cpsr, r0 // CPSR为状态寄存器,用于设置系统运行状态,只能用MSR MRS指令
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) //系统中断重定向于RAM中,以便快速响应中断,搬运的代码为4*16bytes
/* 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
//关闭看门狗
#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 //查阅s3c2440的datesheet中指出寄存器地址
#define pWTCON 0x53000000
#define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
#define INTSUBMSK 0x4A00001C
#define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#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] /* mask all IRQs by setting all bits in the INTMR - default */
mov r1, #0xffffffff //ARM920T有32个中断源,禁止所有中断,32位中断屏蔽寄存器置位
ldr r0, =INTMSK
str r1, [r0]
#if defined(CONFIG_S3C2440)||defined(CONFIG_S3C2410) /* add by zhou */
ldr r1, =0x7ff //屏蔽所有的中断源,S3C2440中寄存器只有前15位有效,故0x7ff置位INTSUNMSK
ldr r0, =INTSUBMSK
str r1, [r0]
#endif
//设置时钟频率
#if defined(CONFIG_S3C2440)
mov r1, #5
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
mov r1, #CLK_CTL_BASE //S3C2440系统主频为405MHZ,USB为48MHZ,要求MPLLCON = (0x7f<<12) | (0x02<<4) | (0x01) = 0x7f021
mov r2, #MDIV_405
add r2, r2, #PSDIV_405
str r2, [r1, #0x04]
#else
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
mov r1, #CLK_CTL_BASE
mov r2, #MDIV_200
add r2, r2,#PSDIV_200
str r2, [r1,#0x04]
#endif
#endif /* (CONFIG_S3C2400) || (CONFIG_S3C2410) || (CONFIG_S3C2440) */
3,关闭MMU和CACHE,并调用lowlevel_init.S完成SDRAM和NANDFLASH的初始化,为代码的重定向做准备
/*******************************************
* we do sys-critical inits only at reboot,
* not when booting from ram!
******************************************/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit //关闭MMU和CACHE,并调用lowlevel_init.S完成SDRAM和NANDFLASH的初始化
#endif
...........
...........
...........
/*
*************************************************************************
*
* 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 //具体设置看下图,详细参考CP15指令:http://blog.csdn.net/gooogleman/article/details/3635238
mcr p15, 0, r0, c7, c7, 0 //向c7写入0将使ICache与DCache 无效flush v3/v4 cache
mcr p15, 0, r0, c8, c7, 0 //向c8写入0将使TLB失效 flush v4 TLB
/*
* disable MMU stuff and caches //协处理器CP15的C1处理器可以设置MMU和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 //由于有两层调用,需要把lr保存到ip,以防止破坏
bl lowlevel_init //调用c函数,初始化FLASH和SDRAM,为代码重定向做准备
mov lr, ip
mov pc, lr //返回
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
代码重定向基本思路:
1.内存运行与否,是则设置堆栈,跳入c函数阶段
2.若不在内存运行,判断是在norflash还在nandflash运行
//代码重定向部分
/***************CHECK_CODE_POSITION******************************/
adr r0, _start /* r0 <- current position of code */ //检查代码是否在已经SDRAM中运行,是则设置堆栈,并跳入c代码部分
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ //_start为u-boot的真正运行地址,_TEXT_BASE为FLASH加载到SDRAM的地址,在config.mk中定义为0x33f80000
cmp r0, r1 /* don't reloc during debug */ //若相等,说明已经在SDRAM中运行,设置堆栈,并且调转到第二阶段的C函数
beq stack_setup //若不相等,则要判断是从NORFLASF或NANDFLASH启动
/***************CHECK_CODE_POSITION******************************/
/***************CHECK_BOOT_FLASH********************************/
ldr r1, =((4<<28)|(3<<4)|(3<<2)) /*address in 4000003c*/
mov r0, #0 //NANDFLASH的启动原理,启动时4K SRAM,即Stepping Stone,会映射到nGCS0,0x0000 0000地址,同时它还是会被映射到0x4000 0000地址
str r0,[r1] //而NORFLASH支持片上运行,并会被一直挂载到nGCS0,0x0000 0000,具体可以参照NANDFLASH启动原理
mov r1, #0x3c /*address in 0x3c*/ //NANDFLASH启动时,因为地址为16倍数对齐,此时0x0000 003c 和 0x4000 003c都为唯一确定的0xdeadbeef,即"Magic Mumber"
ldr r0, [r1] //当0x4000 003c清零,若0x0000 003c读出也是零,则u-boot代码从NANDFLASH启动,否则从NORFLASH
cmp r0, #0
bne relocate
/*recover 0x4000003c */
ldr r0, =(0xdeadbeef) //若在NANDFLASH启动,必须保证代码和前4K拷贝到SRAM一致,否则会进入死循环