Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1024448
  • 博文数量: 238
  • 博客积分: 2842
  • 博客等级: 少校
  • 技术积分: 2765
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-16 00:20
个人简介

stdlf

文章分类

全部博文(238)

文章存档

2013年(6)

2012年(13)

2011年(82)

2010年(89)

2009年(48)

我的朋友

分类:

2009-09-04 22:12:19

uboot代码分析之一(/cpu/start.s)

2008-07-13 20:53

/*

* Startup Code for S3C44B0 CPU-core

* (C) Copyright 2004

*

*/

/*U-BOOT是从cpu/s3c44bo/start.s开始的,这个文件的任务是设置处理器状态,初始化中断和内存时序等,并确定是否需要对整个U-BOOT代码重定位,最终从FLASH中跳转到定位好的内存位置执行*/

#include

#include

/*Jump vector table   ;异常处理向量表

对于复位异常,CPU通过b reset进入复位代码,重新启动系统。发生复位异常通常意味着出现了较严重的错误,必须对系统重新引导。复位代码总在ROM中执行,而b指令可寻址32MB,应付本例中的2MB Flash绰绰有余。其它类型异常则比较自由,如何处理完全由用户决定。因此它们的处理常常在RAM中进行,而本例中RAM挂接在BANK6,地址范围为0x0c00_0000- 0x0c7f_ffff,已经超出了b指令的寻址范围。于是通过ldr指令重置pc*/

 

.globl _start    //系统复位的位置,由u-boot.ld决定,_start声明为外部程序可

//的标签,

_start: b       reset   //在此标签处存放各个异常向量对应的跳转代码

ldr pc,=HandleUndef           ;未定义的指令异常

ldr pc,=HandleSWI             ;软件中断异常

ldr pc,=HandlePabort          ;内存操作异常

ldr pc,=HandleDabort          ;数据异常

b .

ldr pc,=HandleIRQ             ;慢速中断异常

ldr pc,=HandleFIQ             ;快速中断异常

ldr pc,=HandleEINT0 /*mGA H/W interrupt vector table*/

ldr pc,=HandleEINT1           ;外部中断0-8异常

ldr pc,=HandleEINT2

ldr pc,=HandleEINT3

ldr pc,=HandleEINT4567

ldr pc,=HandleTICK /*mGA*/    ;时钟滴答异常

b .

b .

ldr pc,=HandleZDMA0 /*mGB*/   ;两类DMA异常

ldr pc,=HandleZDMA1

ldr pc,=HandleBDMA0

ldr pc,=HandleBDMA1

ldr pc,=HandleWDT             ;看门狗异常

ldr pc,=HandleUERR01 /*mGB*/

b .

b .

ldr pc,=HandleTIMER0 /*mGC*/ ;六个定时器异常

ldr pc,=HandleTIMER1

ldr pc,=HandleTIMER2

ldr pc,=HandleTIMER3

ldr pc,=HandleTIMER4

ldr pc,=HandleTIMER5 /*mGC*/

b .

b .

ldr pc,=HandleURXD0 /*mGD*/   2个串口接受异常

ldr pc,=HandleURXD1

ldr pc,=HandleIIC             IIC 异常         

ldr pc,=HandleSIO             ;同步IO口异常

ldr pc,=HandleUTXD0           2个串口发送异常

ldr pc,=HandleUTXD1 /*mGD*/

b .

b .

ldr pc,=HandleRTC /*mGKA*/    ;实时时钟异常

b .

b .

b .

b .

b . /*mGKA*/

b .

b .

ldr pc,=HandleADC /*mGKB*/    ;模数转换异常

b .

b .

b .

b .

b . /*mGKB*/

b .

b .

;下面是各异常处理对应的函数地址

.equ 在汇编语言中使用,相当于C语言中的#define

.equ HandleReset, 0xc000000          

.equ HandleUndef,0xc000004

.equ HandleSWI, 0xc000008

.equ HandlePabort, 0xc00000c

.equ HandleDabort, 0xc000010

.equ HandleReserved, 0xc000014

.equ HandleIRQ, 0xc000018

.equ HandleFIQ, 0xc00001c

.equ HandleADC, 0xc000020

.equ HandleRTC, 0xc000024

.equ HandleUTXD1, 0xc000028

.equ HandleUTXD0, 0xc00002c

.equ HandleSIO, 0xc000030

.equ HandleIIC, 0xc000034

.equ HandleURXD1, 0xc000038

.equ HandleURXD0, 0xc00003c

.equ HandleTIMER5, 0xc000040

.equ HandleTIMER4, 0xc000044

.equ HandleTIMER3, 0xc000048

.equ HandleTIMER2, 0xc00004c

.equ HandleTIMER1, 0xc000050

.equ HandleTIMER0, 0xc000054

.equ HandleUERR01, 0xc000058

.equ HandleWDT, 0xc00005c

.equ HandleBDMA1, 0xc000060

.equ HandleBDMA0, 0xc000064

.equ HandleZDMA1, 0xc000068

.equ HandleZDMA0, 0xc00006c

.equ HandleTICK, 0xc000070

.equ HandleEINT4567, 0xc000074

.equ HandleEINT3, 0xc000078

.equ HandleEINT2, 0xc00007c

.equ HandleEINT1, 0xc000080

.equ HandleEINT0, 0xc000084

 

.balignl 16,0xdeadbeef    /*.balignl.balign的变体,为伪操作符,控制对齐方式。        它的意思是以当前地址开始,在地址为16的整数倍的地址处的前面插入0xdeadbeef,  这个位置往后,就是干什么的内存,这个位置往前,禁止访问。*/

    /*启动代码(复位后开始执行)如果不做内存初始化,就只建立堆栈,重新装入u-boot    ram跳转到启动的第二阶段

*************************************************************************

* Startup Code (reset vector)

* do important init only if we don't start from memory!

* relocate u-boot to ram

* setup stack

* jump to second stage

*************************************************************************/

 

_TEXT_BASE:

.word TEXT_BASE   

    /*这个值在board\hfrk\hfrks3c44b0\config.mk中定义

TEXT_BASE = 0x0C700000 ,uboot映像在SDRAM中的重定位地址*/

                           

.globl _armboot_start //声明变量_armboot_start为外部程序可访问的标签

_armboot_start:      

.word _start         //在当前位置_armboot_start处写入word型的值_start  

    /*代码段的起始地址也是TEXT_BASE _armboot_start标号处,保存了_start的值, 也就是说,_armboot_start是存放_start的地址,该地址对应的存储单元内容是    0x0C700000*/

 

/* These are defined in the board-specific linker script.*/

.globl _bss_start   

_bss_start:

.word __bss_start

    /*__bss_startuboot bss段起始地址,那么uboot映像的大小就是 __bss_start - _start;由链接程序ld根据链接脚本 \board\hfrk\hfrks3c44b0\u-boot.lds确定*/

 

.globl _bss_end

_bss_end:

.word _end

    //bss段结束地址,由链接程序ld根据链接脚本确定,实际上,_armboot_start并没有实   际意义,它只是在"ldr r2, _armboot_start"中用來寻址_start的值而已,_bss_start 也是一样的道理,真正有意义//的应该是_start __bss_start本身。

 

#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        //IRQ FIQ栈的地址是同一个地址. 系统中不使用IRQ,

//面的代码不用关心在\u-boot\include\configs\hfrks3c44b0.h

//   #undef   CONFIG_USE_IRQ     

#endif

 

    /*上面这段代码,主要保存一些全局变量,用于BOOT程序从FLASH拷贝到RAM,或者其它 的使    用。还有一些变量的长度是通过连接脚本里得到,实际上由编译器算出来的。 */

 

    /*the actual reset code*/

    /** 实际运行的复位代码。从一开始运行的代码,就跳到这里运行。*/

    /* * 设置cpu运行在SVC32模式。*/

reset:

mrs r0,cpsr     /*set the cpu to SVC32 mode*/

    /*取得当前程序状态寄存器cpsrr0

    mrs 读状态寄存器指令,在ARM处理器中只有MRS指令可以将状态寄存器CPSRSPSR 读出到通用寄存器中*/

bic r0,r0,#0x1f

    /*bic 位清除指令,用于清除操作数中为1的位,并把结果放置到目的寄存器中。操作数   1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操 作数232位的掩码,

    31 30 29 28 ---   7   6   -   4    3   2   1   0

    N  Z  C  V        I   F       M4  M3  M2 M1 M0

                                   0   0   0  0   0     User26 模式

                                   0   0   0  0   1     FIQ26 模式

                                   0   0   0  1   0     IRQ26 模式

                                   0   0   0  1   1     SVC26 模式

                                   1   0   0  0   0     User 模式

                                   1   0   0  0   1     FIQ 模式

                                   1   0   0  1   0     IRQ 模式

                                   1   0   0  1   1     SVC 模式

                                   1   0   1  1   1     ABT 模式

                                   1   1   0  1   1     UND 模式

                                   1   1   1  1   1     SYS 模式

 

orr r0,r0,#0x13

    /*ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。r0    014,位置1. M=10011,对应的cpu模式为管理模式.

    通过设置ARMCPSR寄存器,CPU运行在管理模式,为后面进行其它操作作好预备了*/

msr cpsr,r0

 

    //we do sys-critical inits only at reboot,

    // not when booting from ram!

     //重新启动时是否进行初始化, 这个宏要定义在用户的目标板配置头文件中

    // 一般情况下是需要的, 不过, 在移植到其它开发板时可就要注意了!!

    // 在我们的系统中定义了这个宏, 同时我们也是靠这个函数实现了硬件

    // 相关的代码的初始化.

#ifdef CONFIG_INIT_CRITICAL            //\u-boot\include\configs\hfrks3c44b0.h    中定义,因此要执行下面的指令

bl cpu_init_crit     //跳转到该标号执行,见下面。BL 是另一个跳转指令,但跳转之前,  会在寄存器R14中保存PC的当前内容,因此,可以通过将R14 的内容重新加载到PC中, 来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本常用的  手段。

    /* before relocating, we have to setup RAM timing

     * because memory timing is board-dependend, you will

     * find a memsetup.S in your board directory.

*/

 

// 在重新定位之前,要进行RAM访问时间测试,因为每个开发都是不一样的。可以在文件//memsetup.S里看到它的说明

 

Bl memsetup           //memsetup子程序在\board\hfrk\hfrks3c44b0\memsetup.S中实现,                  //参考该文件注释;

#endif

    /*调试阶段的代码是直接在ram中运行的,而最后需要把这些代码固化到FLASH中,因 U-Boot需要自己从Flash转移到RAM中运行。这也是重定向的目的所在

    其中关键的一步就是通过adr   r0, _start得到地址信息r0=_start    =_TEXT_BASE=TEXT_BASE=0x0C700000 。若r0=r1,代码是在ram里运行,不需要重定位;   不然,uboot代码从flash开始运行,这时需要执行copy_loop那段代码了*/

 relocate:     /* relocate U-Boot to RAM     U-Boot重新定位到RAM */

adr         r0, _start          // r0 <- current position of code    r0是代码的                           //当前位置

ldr r1, _TEXT_BASE   //test if we run from flash or RAM 测试判断是从Flash启动,//还是RAM启动

cmp     r0, r1                  // don't reloc during debug        比较r0r1

//调试的时候不要执行重定位

beq     stack_setup               //如果r0等于r1,跳过重定位代码到stack_setup处执行

 

ldr r2, _armboot_start

ldr r3, _bss_start

sub r2, r3, r2   /* r2 <- size of armboot    r2 armboot的大小        */

add r2, r0, r2   /* r2 <- source end address     r2 得到要复制代码的末地址    */

 

copy_loop:

ldmia r0!, {r3-r10}   /* copy from source address [r0]    批量加载指令用于将一片        连续的存储器中的数据传送到多个寄存器 IA   每次传送后地址加1;从源地址[r0]       制 不允许直接存储器地址相互操作,通过r3-r10寄存器进行 */

stmia r1!, {r3-r10}   /* copy to   target address [r1]    将当前代码地址内容复制                        0x0c700000*/

cmp r0, r2    /* until source end addreee [r2]    */

ble copy_loop                  // 检查是否全部复制完

 

    /*now copy to sram the interrupt vector

    发生异常(复位异常除外)后会从ROM的异常向量跳转到RAM*/

adr r0, real_vectors   //加载实际的异常中断向量表,见最后;将该real_vectors表起                            始的地址放到r0

add r2, r0, #1024       //r2=r0+1024 复制real_vectors开始的1024字节内容

ldr r1, =0x0c000000

add r1, r1, #0x08         //r1=0x0c000008

vector_copy_loop:

ldmia r0!, {r3-r10}        

stmia r1!, {r3-r10}                  

 

 

    /*复制0x0c000008开始的目标区域中去,相当于在RAM中建立一个二级跳转表

    因此在0x0c000008处放的是复位指令,0x0c00000b放的是未定义异常处理程序, 0x0c000010放的是软中断异常处理程序。以SWI为例,从0x00000008跳转到0x0c000010   然后跳转到software_interrupt开始执行,最终reset*/

 

cmp r0, r2

ble vector_copy_loop

    /* Set up the stack   接下来设置堆栈, 这里需要注意的是堆栈在代码段的下方,     是上方!!CFG_MALLOC_LENCFG_GBL_DATA_SIZE在目标板的头文件中定  .\u-boot\include\configs\hfrks3c44b0.h */

stack_setup:

ldr r0, _TEXT_BASE   //upper 128 KiB: relocated uboot 上面是128 KiB重定位的u-boot

sub r0, r0, #CFG_MALLOC_LEN /* malloc area   向下是内存分配空间 */

sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo     然后是bdinfo结构体地址空间 */

#ifdef CONFIG_USE_IRQ

sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)       //这里如果需要使用                                       IRQ, 还有给IRQ保留堆栈空间, 一般不使用

#endif

sub sp, r0, #12   /* leave 3 words for abort-stack 给堆栈异常留3个字的空间 */

 

ldr pc, _start_armboot           //最终通过该语句跳转到c代码处执行,stage1的使                                         命就完成了

 

_start_armboot: .word start_armboot

 

 

        /*start_armboot()lib_arm\board.c中定义,它类似于Linux内核的                 start_kernel(),它们都是一种系统初始化的接口函数:在start_kernel()中集中      完成了内核几乎所有资源的初始化,包括CPU相关的资源和外设接口等;而在            start_armboot()中也要完成一些初始化工作。*/

 

 

        /*把引导的汇编看完,已经预备C的运行环境,下面就开始学习C的源程序,             start.S文件跳到文件lib_ARM\board.c里运行. */

/*************************************************************************

* CPU_init_critical registers

* setup important registers

* setup memory timing

*************************************************************************

* CPU_init_critical临界区寄存器

* 设置一些重要的寄存器,并进行内存测试。*/

 

 

    //下面是定义各寄存器的地址

#define INTCON (0x01c00000+0x200000)

#define INTMSK (0x01c00000+0x20000c)

#define LOCKTIME (0x01c00000+0x18000c)

#define PLLCON (0x01c00000+0x180000)

#define CLKCON (0x01c00000+0x180004)

#define WTCON (0x01c00000+0x130000)

#define BDIDES0 0x1f80008

#define BDIDES1 0x1f80028

 

 

        接下来的代码用来关闭看门狗,通过INTMR寄存器屏蔽所有的中断,并配置处理器        内部的时钟频率。

cpu_init_crit:

        /* disable watch dog */   //禁用看门狗;   WTCON看门狗定时器控制寄存器地        址为0x01d30000,第5位为看门狗定时器允许位,为0则禁止看门狗定时器。

ldr r0, =WTCON          

ldr r1, =0x0

str r1, [r0]

        ;使看门狗寄存器WTCON=0x0.

// mask all IRQs by clearing all bits in the INTMRs

ldr r1,=INTMSK

ldr r0, =0x03fffeff 

str r0, [r1]

    //INTMSK中断屏蔽寄存器,除了全局屏蔽位外,其余的26位都分别对应一个中断源。 当屏蔽位为1时,对应的中断被屏蔽,为0,该中断可正常执行。若全局屏蔽位为1,则   所有的中断都不执行。该寄存器的地址为0x01e000c 使INTMSK=0000 0011 1111 1111 1111 1110 1111 1111,第26位为全局屏蔽位=0

    // Set Clock Control Register

ldr r1, =LOCKTIME

ldrb r0, =800

strb r0, [r1]

 

    //LOCKTIME锁时计数寄存器,用于放PLL锁时计数值

ldr r1, =PLLCON

    // 设置锁相环,控制CPU运行速度

 

#if CONFIG_S3C44B0_CLOCK_SPEED==60

ldr r0, =0x88042 /* 60MHz (Quartz=10MHz) */

#elif CONFIG_S3C44B0_CLOCK_SPEED==75

ldr r0, =0xac042 /* 75MHz */

#else

# error CONFIG_S3C44B0_CLOCK_SPEED undefined

#endif

 

str r0, [r1]

ldr r1,=CLKCON

ldr r0, =0x7ff8

str r0, [r1]

    /*CLKCON时钟控制寄存器。CLKCON=0111 1111 1111 1000.打开内部所有模块的钟控

    BDMA设置复位值,BDIDES为初始目的地址寄存器 [31:30] TDM 01=M2IO (从外部存 储器到内部外设)*/

ldr r0, =BDIDES0      

ldr r1, =0x40000000 //BDIDESn reset value should be 0x40000000

str r1, [r0]

 

ldr r0, =BDIDES1     

ldr r1, =0x40000000   //BDIDESn reset value should be 0x40000000

str r1, [r0]

 

mov pc, lr        //返回到bl cpu_init_crit   指令的下面 

 

    /* 实际的中断向量表 */

/*************************************************/

/* interrupt vectors */

/*************************************************/

real_vectors:

b reset

b undefined_instruction

b software_interrupt

b prefetch_abort

b data_abort

b not_used

b irq

b fiq

 

/*************************************************/

 

undefined_instruction:

mov r6, #3

b reset

 

software_interrupt:

mov r6, #4

b reset

 

prefetch_abort:

mov r6, #5

b reset

 

data_abort:

mov r6, #6

b reset

 

not_used:

/* we *should* never reach this */

mov r6, #7

b reset

 

irq:

mov r6, #8

b reset

 

fiq:

mov r6, #9

b reset

 

阅读(944) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~