分类: LINUX
2015-04-03 00:20:08
原文地址:U-Boot启动第一阶段代码分析 作者:mutes
u-boot官方下载地址(包含了各个版本及当前最新版本):
这里我选择下载u-boot-1.1.6.tar.bz2(6402K)
之后的移植我再选择最新版本u-boot-2011.06.tar.bz2(8272K)
U-Boot启动第一阶段代码分析
1:cpu自身的初始化:包括MMU,catch,时钟系统,SDRAM控制系统的初始话。
2:重定位:把自己从flash中搬到SDRAM 中
3:分配堆栈空间,设置堆栈指针
4:清零BSS数据段
5:跳转到第二阶段入口函数。
第一阶段对应的文件是cpu/arm920t/start.S和board/smdk2140/lowlevel_init.S。
根据cpu/arm920t/u-boot.lds中指定的连接方式:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
;指定输出可执行文件是elf格式,32位ARM指令,小端
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
;指定输出可执行文件的平台为ARM
ENTRY(_start)
;指定输出可执行文件的起始代码段为_start
SECTIONS
{
. = 0x00000000; ;从0x0位置开始
. = ALIGN(4); ;代码以4字节对齐
.text : ;指定代码段
{
cpu/arm920t/start.o (.text) ;代码的第一个代码部分
*(.text) ;其它代码部分
}
. = ALIGN(4);
.rodata : { *(.rodata) } ;指定只读数据段
. = ALIGN(4);
.data : { *(.data) } ;指定读\写数据段
. = ALIGN(4);
.got : { *(.got) } ;指定got段,got段是u-boot自定义的一个段,非标准段
. = .;
__u_boot_cmd_start = .; ;把__u_boot_cmd_start赋值为当前位置,即起始位置
.u_boot_cmd : { *(.u_boot_cmd) } ;指定u_boot_cmd段,uboot把所有的uboot命令放在该段
__u_boot_cmd_end = .; ;把__u_boot_cmd_end赋值为当前位置,即bss段的开始位置
. = ALIGN(4);
__bss_start = .; ;把__bss_start赋值为当前位置,即bss段的开始位置
.bss : { *(.bss) } ;指定bss段
_end = .; ;把_end赋值为当前位置,即bss段的结束位置
}
第一个链接的是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开头有如下的代码:
#include
#include
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
@设置异常向量表,其中_start是GNU 汇编的默认入口标签。注意ldr r0,0x1234是把0X1234中的内容写到R0中,ldr r0,=0x1234,是将1234这个值写到R0中,以及ADR 是用来加载地址。
.globl _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 /* irq中断向量 */
ldr pc, _fiq /* fiq中断向量 */
/* 中断向量表入口地址 */
@.word为GUN 汇编分配一段字内存单元,下面几句话相当于是C语言中的变量名和变量值。
_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
.balignl 16,0xdeadbeef
以上代码设置了ARM异常向量表,各个异常向量介绍如下:
在cpu/arm920t/start.S中还有这些异常对应的异常处理程序。当一个异常产生时,CPU根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就跳转到对应的异常处理程序执行。
其中复位异常向量的指令“b reset”决定了U-Boot启动后将自动跳转到标号“reset”处执行。
/*
*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/
@全局符号定义
_TEXT_BASE:
.word TEXT_BASE @在board/smdk2410/config.mk中定义为3ff80000..即UBOOT映像文件所在地址
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
@下面主要在u-boot.lds链接脚本中定义
.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
(2)设置CPU进入SVC模式
/*
* the actual reset code
系统上电或reset后,cpu的PC一般都指向0x0地址,在0x0地址上的指令是
*/
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr //将CPSR状态寄存器读取,保存到R0中
bic r0,r0,#0x1f /*工作模式位清零 */
orr r0,r0,#0xd3 /*工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置1 */
msr cpsr,r0
以上代码将CPU的工作模式位设置为管理模式,并将中断禁止位和快中断禁止位置‘1’,从而屏蔽了IRQ和FIQ中断。
(3)设置控制寄存器地址
/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410) /* s3c2410与s3c2440下面4个寄存器地址相同 */
# define pWTCON 0x53000000 /* WATCHDOG控制寄存器地址 */
# define INTMSK 0x4A000008/*中断子屏蔽寄存器地址*/
# define INTSUBMSK 0x4A00001C /* 中断子屏蔽寄存器地址 */
# define CLKDIVN 0x4C000014 /* clock divisor register CLKDIVN寄存器地址, 用来设置FCLK,HCLK,PCLK三者的比例。*/
#endif
对与s3c2410开发板,以上代码完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四个寄存器的地址的设置。各个寄存器地址参见数据手册。这里如果移植到2440,将#elif defined(CONFIG_S3C2410)修改为#elif defined(CONFIG_S3C2440)即可,因为s3c2410与s3c2440这4个寄存器地址是相同的。
(4)关闭看门狗
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0] /* 看门狗控制器的最低位为0时,看门狗不输出复位信号 */
以上代码向看门狗控制寄存器写入0,关闭看门狗。否则在U-Boot启动过程中,CPU将不断重启。如果移植到2440,这里一样。
(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所处理。
根据数据手册,INTMSK寄存器是一个32位的寄存器,每位对应一个中断,向其中写入0xffffffff就将INTMSK寄存器全部位置一,从而屏蔽对应的中断。
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
INTSUBMSK每一位对应子中断屏蔽寄存器SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是否被CPU所处理。
根据数据手册,INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低10位。向其中写入0x3ff就是将INTSUBMSK寄存器全部有效位(低10位)置一,从而屏蔽对应的中断。注意:对于2440,这里写入的是0x7fff,根据2440数据手册,它使用了低15位。向其中写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)置一,从而屏蔽对应的中断。
(6) 设置时钟分频控制寄存器。
/* FCLK:HCLK:PCLK = 1:2:4 * /FCLK为核心提供时钟,HCLK为AHB(ARM920T,内存控制器,中断控制器,LCD控制器,DMA和主USB模块)提供时钟,PCLK为APB(看门狗、IIS、I2C、PWM、MMC、ADC、UART、GPIO、RTC、SPI)提供时钟
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
(7)关闭MMU,cache,执行CPU初始话。
接着往下看:
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit @如果没有定义CONFIG_SKIP_LOELEVEL_INIT,则执行cpu_init_crit.
#endif
cpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动则应该注释掉这段代码。后面再对它分析。
@重新定位u-boot到SDRAM中
这里将整个U-Boot的代码(包括第一、第二阶段)都复制到SDRAM中,这在cpu/arm920t/start.S中实现:
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM将U-Boot复制到RAM中 */
adr r0, _start /* r0 <- current position of code r0 = 当前代码的开始地址 */
@判断U-Boot是否是下载到RAM中运行,若是,则不用再复制到RAM中了,这种情况通常在调试U-Boot时才发生,通过adr指令得到当前代码的地址信息:如果U-boot是从RAM开始运行,则从adr,r0,_start得到的地址信息为 r0=_start=_TEXT_BASE=TEXT_BASE=0x3ff80000;如果U-boot从Flash开始运行,即从处理器对应的地址运行,则r0=0x0000,这时将会执行copy_loop标识的那段代码了。
ldr r1, _TEXT_BASE /* r1 =代码段的连接地址test if we run from flash or RAM */
cmp r0, r1 /*测试现在是在Flash中还是在RAM中,_start等于_TEXT_BASE说明是在RAM中运行,路过搬移代码 */
beq stack_setup @如果r0等于r1,已经在RAM中(这通常是调试时,直接下载到RAM中),则不需要复制。
ldr r2, _armboot_start @_armboot_start在前面定义,是第一条指令的运行地址即_start的内容写入r2
ldr r3, _bss_start @在连接脚本u-boot.lds中定义,是代码段的结束地址
sub r2, r3, r2 /* @计算armboot所占字节大小 ,r2=代码段长度*/
add r2, r0, r2 /* r2 <- source end address @armboot结束地址,r2 = NOR Flash上代码段的结束地址*/
copy_loop: @实现从flash中拷贝到_TEXT_BASE(0x3ff80000)所在的地址中去
ldmia r0!, {r3-r10} /* copy from source address [r0] ,从地址[r0]处获得数据,即从地址为[r0]的NOR Flash中读入8个字的数据 */
stmia r1!, {r3-r10} /* copy to target address [r1] 将r3至r10寄存器的数据复制给地址为[r1]处 */
cmp r0, r2 /* until source end addreee [r2] 判断是否复制完毕*/
ble copy_loop /* 没复制完,则继续 */
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* Set up the stack
@设置堆栈 */
栈的设置灵活性很大,只要让sp寄存器指向一段没有使用的内存即可。
stack_setup:
ldr r0, _TEXT_BASE /* _TEXT_BASE为代码段的开始地址,值为0x33F80000 upper 128 KiB: relocated uboot*/
sub r0, r0, #CFG_MALLOC_LEN /* malloc area@代码段下面,留出一段内存以实现malloc */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo再留出一段内存,存一些全局参数,这里跳过这个全局数据区 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) @为IRQ,FIQ留出空间,分配IRQ、FIQ模式的栈
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack @最后,留出12字节的内存给abort异常,往下的内存就都是栈了,将当前的地址赋给sp,这样就为内存栈设置好了,之后如果在u-boot中运行程序时需要使用栈的时候就从这里开始。 */
到了这一步,读者可以知道内存的使用情况了,如下图所示(图中与上面的划分稍有不同,这是因为在cpu/arm920t/cpu.c中的cpu_init函数中才真正为IRQ、FIQ模式划分了栈)
U-boot内存使用情况:
(5)跳转到第二阶段代码的C入口点。
在跳转之前,还要清除BSS段(初始值为0、无初始值的全局变量、静态变量放在BSS段),代码如下:
clear_bss: @清空用户堆区
ldr r0, _bss_start /* find start of bss segment ,BSS段的开始地址,它的值在连接脚本u-boot.lds中确定 */
ldr r1, _bss_end /* stop here ,BSS段的结束地址,它的值在连接脚本u-boot.lds中确定*/
mov r2, #0x00000000 /* clear */往r2中写入0值
clbss_l:str r2, [r0] /* clear loop... 往BSS段中写入0值 */
add r0, r0, #4
cmp r0, r1 /*判断是否清空完毕*/
ble clbss_l /* 没清空完,则继续 */
#if 0
/* try doing this stuff after the relocation */
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMR
str r1, [r0]
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/* END stuff after relocation */
#endif
@现在,C函数的运行环境已经完全准备好,通过如下命令直接跳转(这之后,程序才在内存中执行),它将调用lib_arm/board.c中的start_armboot函数,这是第二阶段的入口点,这样就到了u-boot的第二阶段了。
ldr pc, _start_armboot
_start_armboot: .word start_armboot
下面分析一下cpu_init_crit到底做了什么:
cpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动则应该注释掉这段代码。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches 使数据cache与指令cache无效
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache 向c7写入0将使ICache与DCache无效*/
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB 向c8写入0将使TLB失效*/
代码中的c0,c1,c7,c8都是ARM920T的协处理器CP15的寄存器。其中c7是cache控制寄存器,c8是TLB控制寄存器。上面二行代码将0写入c7、c8,使Cache,TLB内容无效。
/*
* disable MMU stuff and caches关闭mmu和cache
*/
@MRC指令的格式为:
@MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
@MRC指令用于将协处理器寄存器中的数据传送到ARM处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器为ARM处理器的寄存器,源寄存器1和源寄存器2均为协处理器的寄存器。 指令示例: MRC P3,3,R0,C4,C5,6 ;该指令将协处理器P3的寄存器中的数据传送到ARM处理器寄存器中。
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS), 将13,9,8bit清零(13—异常向量表基地址:0x0, 9—Disable System Protection, 8—Disable ROM Protection)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM), 将7,2,1,0bit清零(7—为0 的时候表示小端字节序,2-- Data Cache Disabled,1-- Alignment Fault checking disabled,0—为0的话MMU disabled)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align, 将bit 1 设置为1表示Fault checking enabled
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache, 将bit 12设置为1表示使能 I-Cache。
mcr p15, 0, r0, c1, c0, 0 @保存r0到控制寄存器
这几行代码关闭了MMU。这是通过修改CP15的c1寄存器来实现的,将c1的 M位置零,关闭了MMU。先看CP15的c1寄存器的格式(仅列出代码中用到的位):
表 2.3 CP15的c1寄存器格式(部分)
各个位的意义如下:
V : 表示异常向量表所在的位置,0:异常向量在0x00000000;1:异常向量在 0xFFFF0000
I : 0 :关闭ICaches;1 :开启ICaches
R、S : 用来与页表中的描述符一起确定内存的访问权限
B : 0 :CPU为小字节序;1 : CPU为大字节序
C : 0:关闭DCaches;1:开启DCaches
A : 0:数据访问时不进行地址对齐检查;1:数据访问时进行地址对齐检查
M : 0:关闭MMU;1:开启MMU
/*
* 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.
*/
在将u-boot搬移到ram前,先要完成其中内存初始化的工作,lowlevel_init就完成了内存初始化的工作,由于内存初始化是依赖于开发板的,因此lowlevel_init的代码一般放在board下面相应的目录中。对于2410,lowlevel_init在board/smdk2410/lowlevel_init.S中。
mov ip, lr @保存当前链接寄存器中的值
bl lowlevel_init @主要是初始话存储控制器件即初始化内存,共13个。只需要设置BWSCON和BANKCONx(x为0-5),而BANK6,BANK7接SDRAM,除了设置 BWSCON和BANKCONx(x为6,7),还需要设置其他四个寄存器,而这13个寄存器的地址是连续的,BWSCON是第一个寄存器。
mov lr, ip
mov pc, lr @返回执行relocate
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
下面分析一下lowlevel_init,代码如下:
#include
#include
/* some parameters for the board */
/*
*
* Taken from linux/arch/arm/boot/compressed/head-s3c2410.S
*
* Copyright (C) 2002 Samsung Electronics SW.LEE
*
*/
#define BWSCON 0x48000000 /* 13个存储控制器的开始地址 */
/* BWSCON */
#define DW8 (0x0)
#define DW16 (0x1)
#define DW32 (0x2)
#define WAIT (0x1<<2)
#define UBLB (0x1<<3)
#define B1_BWSCON (DW32)
#define B2_BWSCON (DW16)
#define B3_BWSCON (DW16 + WAIT + UBLB)
#define B4_BWSCON (DW16)
#define B5_BWSCON (DW16)
#define B6_BWSCON (DW32)
#define B7_BWSCON (DW32)
/* BANK0CON */
#define B0_Tacs 0x0 /* 0clk */
#define B0_Tcos 0x0 /* 0clk */
#define B0_Tacc 0x7 /* 14clk */
#define B0_Tcoh 0x0 /* 0clk */
#define B0_Tah 0x0 /* 0clk */
#define B0_Tacp 0x0
#define B0_PMC 0x0 /* normal */
/* BANK1CON */
#define B1_Tacs 0x0 /* 0clk */
#define B1_Tcos 0x0 /* 0clk */
#define B1_Tacc 0x7 /* 14clk */
#define B1_Tcoh 0x0 /* 0clk */
#define B1_Tah 0x0 /* 0clk */
#define B1_Tacp 0x0
#define B1_PMC 0x0
#define B2_Tacs 0x0
#define B2_Tcos 0x0
#define B2_Tacc 0x7
#define B2_Tcoh 0x0
#define B2_Tah 0x0
#define B2_Tacp 0x0
#define B2_PMC 0x0
#define B3_Tacs 0x0 /* 0clk */
#define B3_Tcos 0x3 /* 4clk */
#define B3_Tacc 0x7 /* 14clk */
#define B3_Tcoh 0x1 /* 1clk */
#define B3_Tah 0x0 /* 0clk */
#define B3_Tacp 0x3 /* 6clk */
#define B3_PMC 0x0 /* normal */
#define B4_Tacs 0x0 /* 0clk */
#define B4_Tcos 0x0 /* 0clk */
#define B4_Tacc 0x7 /* 14clk */
#define B4_Tcoh 0x0 /* 0clk */
#define B4_Tah 0x0 /* 0clk */
#define B4_Tacp 0x0
#define B4_PMC 0x0 /* normal */
#define B5_Tacs 0x0 /* 0clk */
#define B5_Tcos 0x0 /* 0clk */
#define B5_Tacc 0x7 /* 14clk */
#define B5_Tcoh 0x0 /* 0clk */
#define B5_Tah 0x0 /* 0clk */
#define B5_Tacp 0x0
#define B5_PMC 0x0 /* normal */
#define B6_MT 0x3 /* SDRAM */
#define B6_Trcd 0x1
#define B6_SCAN 0x1 /* 9bit */
#define B7_MT 0x3 /* SDRAM */
#define B7_Trcd 0x1 /* 3clk */
#define B7_SCAN 0x1 /* 9bit */
/* REFRESH parameter */
#define REFEN 0x1 /* Refresh enable */
#define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */
#define Trp 0x0 /* 2clk */
#define Trc 0x3 /* 7clk */
#define Tchr 0x2 /* 3clk */
#define REFCNT 1113 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
/**************************************/
_TEXT_BASE:
.word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1 /* SMRDATA减 _TEXT_BASE就是13个寄存器的偏移地址 */
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
0:
ldr r3, [r0], #4 /*将13个寄存器的值逐一赋值给对应的寄存器*/
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
/* 下面是13个寄存器的值 */
SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word 0x32
.word 0x30
.word 0x30
lowlevel_init初始化了13个寄存器来实现RAM时钟的初始化。lowlevel_init函数对于U-Boot从NAND Flash或NOR Flash启动的情况都是有效的。
board/samsung/mini2440/lowlevel_init.o将被链接到cpu/arm920t/start.o 后面,因此board/samsung/mini2440/lowlevel_init.o也在U-Boot的前4KB的代码中。
U-Boot.lds链接脚本有如下代码:
.text :
{
cpu/arm920t/start.o (.text)
board/samsung/mini2440/lowlevel_init.o (.text)
board/samsung/mini2440/nand_read.o (.text)
… …
}
U-Boot在NAND Flash启动时,lowlevel_init.o将自动被读取到CPU内部4KB的内部RAM中。因此lowlevel_init函数代码将从CPU内部RAM中复制寄存器的值到相应的寄存器中。
对于U-Boot在NOR Flash启动的情况,由于U-Boot连接时确定的地址是U-Boot在内存中的地址,而此时U-Boot还在NOR Flash中,因此还需要在NOR Flash中读取数据到RAM中。
由于NOR Flash的开始地址是0,而U-Boot的加载到内存的起始地址是TEXT_BASE,SMRDATA标号在Flash的地址就是SMRDATA-TEXT_BASE。
综上所述,lowlevel_init的作用就是将SMRDATA开始的13个值复制给开始地址[BWSCON]的13个寄存器,从而完成了存储控制器的设置。
Start.S文件之后是
@.macro伪操作符标识宏定义的开始,.endm标识宏定义的结束。二者包含的一段代码,称为宏定义体,这样在程序中就可通过宏指令多次调用该代码段。格式:
.macro macroname {parameter{,parameter}...}
...
.endm
宏的参数可直接使用斜线“/字符”来引用,如下“/reg”所示。
@以下就是各种中断的处理。
/**************** 异常处理程序 *******************/
.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\
……………………………..
@略过