U-Boot启动过程完全分析
Posted on 2010-07-17 21:31 heaad 阅读(2992) 评论(0) 编辑 收藏
1.1 U-Boot 工作过程
U-Boot 启动内核的过程可以分为两个阶段,两个阶段的功能如下:
(1)第一阶段的功能
硬件设备初始化
加载 U-Boot 第二阶段代码到 RAM 空间
设置好栈
跳转到第二阶段代码入口
(2)第二阶段的功能
初始化本阶段使用的硬件设备
检测系统内存映射
将内核从 Flash 读取到 RAM 中
为内核设置启动参数
调用内核
1.1.1 U-Boot 启动第一阶段代码分析
第一阶段对应的文件是 cpu/arm920t/start.S 和 board/samsung/mini2440/lowlevel_init.S。
U-Boot 启动第一阶段流程如下:
图 2.1 U-Boot 启动第一阶段流程
根据 cpu/arm920t/u-boot.lds 中指定的连接方式:
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
board/samsung/mini2440/lowlevel_init.o (.text)
board/samsung/mini2440/nand_read.o (.text)
*(.text)
}
… …
}
第一个链接的是 cpu/arm920t/start.o,因此 u-boot.bin 的入口代码在 cpu/arm920t/start.o 中,其源代码在
cpu/arm920t/start.S 中。下面我们来分析 cpu/arm920t/start.S 的执行。
1. 硬件设备初始化
(1)设置异常向量
cpu/arm920t/start.S 开头有如下的代码:
.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
/* 软件中断向量 */
/* 预取指令异常向量 */
/* 数据操作异常向量 */
/* 未使用 */
/* irq 中断向量 */
/* 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
.word irq
.word fiq
_irq:
_fiq:
.balignl 16,0xdeadbeef
以上代码设置了 ARM 异常向量表,各个异常向量介绍如下:
表 2.1 ARM 异常向量表
地址
异常
进入模式
描述
0x00000000 复位
管理模式
复位电平有效时,产生复位异常,程序跳转
到复位处理程序处执行
0x00000004 未定义指令 未定义模式 遇到不能处理的指令时,产生未定义指令异
常
0x00000008 软件中断 管理模式
执行 SWI 指令产生,用于用户模式下的程序
调用特权操作指令
0x0000000c 预存指令 中止模式
处理器预取指令的地址不存在,或该地址不
允许当前指令访问,产生指令预取中止异常
0x00000010 数据操作 中止模式
处理器数据访问指令的地址不存在,或该地
址不允许当前指令访问时,产生数据中止异
常
0x00000014 未使用
未使用
未使用
0x00000018
IRQ
IRQ
外部中断请求有效,且 CPSR 中的 I 位为 0 时,
产生 IRQ 异常
0x0000001c
FIQ
FIQ
快速中断请求引脚有效,且 CPSR 中的 F 位为
0 时,产生 FIQ 异常
在 cpu/arm920t/start.S 中还有这些异常对应的异常处理程序。当一个异常产生时,CPU 根据异常号在异常向量表
中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU 就跳转到对应的异常处理程序执行。
其中复位异常向量的指令“b start_code”决定了 U-Boot 启动后将自动跳转到标号“start_code”处执行。
(2)CPU 进入 SVC 模式
start_code:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
/*工作模式位清零 */
orr r0, r0, #0xd3
/*工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置 1
msr cpsr, r0
以上代码将 CPU 的工作模式位设置为管理模式,并将中断禁止位和快中断禁止位置一,从而屏蔽了 IRQ 和 FIQ 中
*/
断。
(3)设置控制寄存器地址
# if defined(CONFIG_S3C2400)
# define pWTCON
0x15300000
# define INTMSK
0x14400008
# define CLKDIVN
0x14800014
#else /* s3c2410 与 s3c2440 下面 4 个寄存器地址相同 */
# define pWTCON
0x53000000
# define INTMSK
0x4A000008
/* WATCHDOG 控制寄存器地址 */
/* INTMSK 寄存器地址 */
# define INTSUBMSK 0x4A00001C /* INTSUBMSK 寄存器地址 */
# define CLKDIVN
0x4C000014
/* CLKDIVN 寄存器地址 */
# endif
对与 s3c2440 开发板,以上代码完成了 WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN 四个寄存器的地址的设置。各个
寄存器地址参见参考文献[4] 。
(4)关闭看门狗
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0] /* 看门狗控制器的最低位为 0 时,看门狗不输出复位信号 */
以上代码向看门狗控制寄存器写入 0,关闭看门狗。否则在 U-Boot 启动过程中,CPU 将不断重启。
(5)屏蔽中断
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff /* 某位被置 1 则对应的中断被屏蔽 */
ldr r0, =INTMSK
str r1, [r0]
INTMSK 是主中断屏蔽寄存器,每一位对应 SRCPND(中断源引脚寄存器)中的一位,表明 SRCPND 相应位代表的中
断请求是否被 CPU 所处理。
根据参考文献 4,INTMSK 寄存器是一个 32 位的寄存器,每位对应一个中断,向其中写入 0xffffffff 就将 INTMSK
寄存器全部位置一,从而屏蔽对应的中断。
# if defined(CONFIG_S3C2440)
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
INTSUBMSK 每一位对应 SUBSRCPND 中的一位,表明 SUBSRCPND 相应位代表的中断请求是否被 CPU 所处理。
根据参考文献 4,INTSUBMSK 寄存器是一个 32 位的寄存器,但是只使用了低 15 位。向其中写入 0x7fff 就是将
INTSUBMSK 寄存器全部有效位(低 15 位)置一,从而屏蔽对应的中断。
(6)设置 MPLLCON,UPLLCON, CLKDIVN
# if defined(CONFIG_S3C2440)
#define MPLLCON 0x4C000004
#define UPLLCON 0x4C000008
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
ldr r0, =MPLLCON
ldr r1, =0x7F021
str r1, [r0]
ldr r0, =UPLLCON
ldr r1, =0x38022
str r1, [r0]
# else
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
CPU 上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU 开始执行指令。但实际上,FCLK 可以高于 Fin,
为了提高系统时钟,需要用软件来启用 PLL。这就需要设置 CLKDIVN,MPLLCON,UPLLCON 这 3 个寄存器。
CLKDIVN 寄存器用于设置 FCLK,HCLK,PCLK 三者间的比例,可以根据表 2.2 来设置。
表 2.2 S3C2440 的 CLKDIVN 寄存器格式
CLKDIVN
位
说明
HDIVN
[2:1] 00 : HCLK = FCLK/1.
01 : HCLK = FCLK/2.
初始值
00
10 : HCLK = FCLK/4 (当 CAMDIVN[9] = 0 时)
HCLK= FCLK/8 (当 CAMDIVN[9] = 1 时)
11 : HCLK = FCLK/3 (当 CAMDIVN[8] = 0 时)
HCLK = FCLK/6 (当 CAMDIVN[8] = 1 时)
PDIVN
[0]
0: PCLK = HCLK/1 1: PCLK = HCLK/2
0
设置 CLKDIVN 为 5,就将 HDIVN 设置为二进制的 10,由于 CAMDIVN[9]没有被改变过,取默认值 0,因此 HCLK = FCLK/4。
PDIVN 被设置为 1,因此 PCLK= HCLK/2。因此分频比 FCLK:HCLK:PCLK = 1:4:8 。
MPLLCON 寄存器用于设置 FCLK 与 Fin 的倍数。MPLLCON 的位[19:12]称为 MDIV,位[9:4]称为 PDIV,位[1:0]称为
SDIV。
对于 S3C2440,FCLK 与 Fin 的关系如下面公式:
MPLL(FCLK) = (2×m×Fin)/(p×
)
其中: m=MDIC+8,p=PDIV+2,s=SDIV
MPLLCON 与 UPLLCON 的值可以根据参考文献 4 中“PLL VALUE SELECTION TABLE”设置。该表部分摘录如下:
表 2.3 推荐 PLL 值
输入频率
输出频率
MDIV
PDIV
SDIV
12.0000MHz
48.00 MHz
56(0x38)
12.0000MHz
405.00 MHz
127(0x7f)
2
2
2
1
当 mini2440 系统主频设置为 405MHZ,USB 时钟频率设置为 48MHZ 时,系统可以稳定运行,因此设置 MPLLCON 与
UPLLCON 为:
MPLLCON=(0x7f<<12) | (0x02<<4) | (0x01) = 0x7f021
UPLLCON=(0x38<<12) | (0x02<<4) | (0x02) = 0x38022
(7)关闭 MMU,cache
接着往下看:
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl
cpu_init_crit
#endif
cpu_init_crit 这段代码在 U-Boot 正常启动时才需要执行,若将 U-Boot 从 RAM 中启动则应该注释掉这段代码。
下面分析一下 cpu_init_crit 到底做了什么:
320 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
321 cpu_init_crit:
322 /*
323 * 使数据 cache 与指令 cache 无效 */
324 */
325 mov r0, #0
326 mcr p15, 0, r0, c7, c7, 0
/* 向 c7 写入 0 将使 ICache 与 DCache 无效*/
327 mcr p15, 0, r0, c8, c7, 0
/* 向 c8 写入 0 将使 TLB 失效 */
328
329 /*
330 * disable MMU stuff and caches
331 */
332 mrc p15, 0, r0, c1, c0, 0
/* 读出控制寄存器到 r0 中 */
333 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
334 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
335 orr r0, r0, #0x00000002 @ set bit 2 (A) Align
336 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
337 mcr p15, 0, r0, c1, c0, 0
/* 保存 r0 到控制寄存器 */
338
339 /*
340 * before relocating, we have to setup RAM timing
341 * because memory timing is board-dependend, you will
342 * find a lowlevel_init.S in your board directory.
343 */
344 mov ip, lr