u-boot存储器映射图:

start.S 代码分析


#include <common.h>
#include <config.h>/*#include <config.h>头文件中声明的以下三个头文件,加一个CONFIG_BOARDDIR声明
#define CONFIG_BOARDDIR board/tekkamanninja/mini2440
#include <config_defaults.h>
#include <configs/mini2440.h>
#include <asm/config.h>
*/.globl _start /*定义一个编译器使用的全局变量*/ //不占内存
_start: b   start_code          /*复位向量,上电后执行的第一条指令(这里要用b相对跳转指令,因为编译时的链接地址不为0,而是0x3*******)*///占4字节内存ldr  pc, _undefined_instruction  /*未定义指令异常,0x04(属于绝对跳转,跳转到undefined_instruction标签地址值处)*/        //占4字节内存ldr pc, _software_interrupt     /*软中断异常,0x08*/      //占4字节内存ldr pc, _prefetch_abort /*内存操作异常,0x0c*/         //占4字节内存ldr pc, _data_abort /*数据异常,0x10*/               //占4字节内存ldr pc, _not_used   /*未使用,0x14*/            //占4字节内存ldr pc, _irq        /*慢速中断异常,0x18*/ //占4字节内存ldr pc, _fiq        /*快速中断异常,0x1c*/ //占4字节内存_undefined_instruction: .word undefined_instruction   /*取得undefined_instruction标签地址值(word型),_undefined_instruction引用了该地址*///占4字节内存
_software_interrupt:    .word software_interrupt    //占4字节内存
_prefetch_abort:    .word prefetch_abort    //占4字节内存
_data_abort:        .word data_abort    //占4字节内存
_not_used:      .word not_used  //占4字节内存
_irq:           .word irq   //占4字节内存
_fiq:           .word fiq   //占4字节内存/*
用途:后会用这个地址的内容来判断是否是从norflash启动(软件自动识别启动方式,自动选择代码拷贝方案,norflash和nandflash的拷贝方式不同)
15*4=60 前面占用了60字节地址,所以.balignl伪指令占用4个字节地址。就能使16字节对齐了
*/.balignl 16,0xdeadbeef        //找到第一次出现的以16为整数倍的地址,占4字节内存就能使16字节对齐了//占4字节内存/*此处地址为@0x00000016  *//*
*注意:0xdeadbeef是什么意思?
*类似这样的值很多,像0xabababab,它们的作用就是为内存做标记,插在那里,就表示从这个位置往后的一段有特殊作用的内存。
*//**************************************************************************** Startup Code (called from the ARM reset exception vector)** do important init only if we don't start from memory!* relocate armboot to ram* setup stack* jump to second stage***************************************************************************/
@这里的 TEXT_BASE = 0x33F80000 ,_start= 0x33F80000 (都是代码段的启始链接地址,代码运行时的地址)//反汇编查看uboot.elf可以看到_TEXT_BASE:               .word   TEXT_BASE   /*把TEXT_BASE值保存在当前地址,TEXT_BASE是你设置的代码段的启始链接地址*/  //占4字节内存.globl _armboot_start   /*暴露全局,编译时_armboot_start可以被其他文件引用*/ //不占内存
_armboot_start:.word _start     /*把_start值保存在当前地址,_start是你设置的代码段的启始链接地址*/  //占4字节内存/** These are defined in the board-specific linker script.*/@__bss_start定义在和开发板相关的u-boot.lds中,_bss_start保存的是__bss_start标号所在的地址。
.globl _bss_start
_bss_start:.word __bss_start    /*把__bss_start值保存在当前地址,__bss_start是你设置的bbs段的启始地址*/  //占4字节内存(去反汇编得到地址为0x33fb86c4,由编译器计算得到).globl _bss_end
_bss_end:.word _end     /*把_end值保存在当前地址,_end是你设置的bbs段的结束地址*/  //占4字节内存   (去反汇编得到地址为0x33ffb49c,由编译器计算得到)@中断的堆栈设置
#ifdef CONFIG_USE_IRQ       /*是否声明了要使用中断功能(mini2440配置文件在include/configs/mini2440.h)*/
/* 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* 设置管理员( SVC)模式*/mrs   r0, cpsrbic r0, r0, #0x1forr    r0, r0, #0xd3msr    cpsr, r0@  bl  coloured_LED_init
@  bl  red_LED_on@针对AT91RM9200进行特殊处理
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)/** relocate exception table*/ldr   r0, =_startldr r1, =0x0mov    r2, #16
copyex:subs r2, r2, #1ldr   r3, [r0], #4str r3, [r1], #4bne copyex
#endif@针对CONFIG_S3C24X0类别的CPU进行配置
#ifdef CONFIG_S3C24X0/* turn off the watchdog */@S3C24X0相关寄存器的定义
# if defined(CONFIG_S3C2400)  /*s3c2400没有次中断寄存器*/
#  define pWTCON    0x15300000  /*pWTCON定义为看门狗控制寄存器的地址*/
#  define INTMSK    0x14400008  /* INTMSK定义为主中断屏蔽寄存器的地址 */
#  define CLKDIVN   0x14800014  /* CLKDIVN定义为时钟分频控制寄存器的地址 */
#else
#  define pWTCON    0x53000000  /*pWTCON定义为看门狗控制寄存器的地址*/
#  define INTMSK    0x4A000008  /* INTMSK定义为主中断屏蔽寄存器的地址 */
#  define INTSUBMSK 0x4A00001C  /*INTSUBMSK定义为次中断屏蔽寄存器的地址*/
#  define CLKDIVN   0x4C000014  /* CLKDIVN定义为时钟分频控制寄存器的地址 */
# endif
@至此寄存器地址设置完毕/*PLL配置相关参数定义*/
#define CLK_CTL_BASE    0x4C000000  /*PLL 锁定时间计数寄存器             tekkaman  */
@输出频率为405MHZ的配置
#define MDIV_405    0x7f << 12    /*主分频器控制          tekkaman */
#define PSDIV_405   0x21        /*预分频后分频器控制           tekkaman */
@输出频率为200MHZ的配置
#define MDIV_200    0xa1 << 12    /*主分频器控制          tekkaman */
#define PSDIV_200   0x31        /*预分频后分频器控制           tekkaman */@关闭看门狗ldr r0, =pWTCONmov r1, #0x0str r1, [r0]@关中断/** mask all IRQs by setting all bits in the INTMR - default*/mov  r1, #0xffffffffldr  r0, =INTMSKstr r1, [r0]
@关次中断
# if defined(CONFIG_S3C2410)ldr r1, =0x7ffldr  r0, =INTSUBMSKstr  r1, [r0]
# endif#if defined(CONFIG_S3C2440)ldr   r1, =0x7fff    ldr r0, =INTSUBMSKstr  r1, [r0]
#endif#if defined(CONFIG_S3C2440)
/*配置FCLK:HCLK:PCLK的分频比*//* FCLK:HCLK:PCLK = 1:4:8 */ldr    r0, =CLKDIVNmov    r1, #5str   r1, [r0]/*CP15协处理器配置,CP15的C1寄存器各种控制位配置(mmu的协处理器)*/   mrc p15, 0, r1, c1, c0, 0   /*将CP15的寄存器C1的值读到r0中*/orr   r1, r1, #0xc0000000     /*使能MMU或者PU,使能地址对齐检查*/mcr   p15, 0, r1, c1, c0, 0   /*将r0的值写到CP15的寄存器C1中 tekkaman*/ /*配置倍频,pll配置,405MHZ*/ mov r1, #CLK_CTL_BASE   /*PLL 锁定时间计数寄存器             tekkaman  */mov r2, #MDIV_405   add r2, r2, #PSDIV_405  str r2, [r1, #0x04]     /* MPLLCON tekkaman */#else/* FCLK:HCLK:PCLK = 1:2:4 *//* default FCLK is 120 MHz ! */ldr  r0, =CLKDIVNmov    r1, #3str   r1, [r0]/*CP15协处理器配置,CP15的C1寄存器各种控制位配置(具体查看手册:《ARM920TTechnical Reference Manual》  搜索:“   Register 1”)
手册上有这样一段话:
如果 HDIVN 不为 0,CPU 总线模式应该使用以下指令使其从快总线模式改变为异步总线模式(S3C2440
不支持同步总线模式)。
MMU_SetAsyncBusMode
MRC  p15, 0,  r0,  c1,  c0,  0
ORR  r0,  r0,  #R1_nF:OR:R1_iA
MCR  p15, 0,  r0,  c1,  c0,  0
如果 HDIVN 不为 0 并且 CPU 总线模式为快总线模式,CPU 运行在 HCLK。可以用此特性在不影响 HCLK 和
PCLK 的情况下改变 CPU 频率为一半或更多。*/ mrc p15, 0, r1, c1, c0, 0   /*将CP15的寄存器C1的值读到r0中*/orr   r1, r1, #0xc0000000     /*其中[31:30]bit是与“FastBus mode/Asynchronous”相关的。如果要切换到“Asynchronous”模式,则“iA=1;nF=1”,所以“#R1_nF:OR:R1_iA”就表示“0xc0000000”。*/mcr p15, 0, r1, c1, c0, 0   /*将r0的值写到CP15的寄存器C1中 tekkaman*/ /*配置倍频,pll配置,405MHZ*/     mov r1, #CLK_CTL_BASE   /* tekkaman*/mov    r2, #MDIV_200   add r2, r2, #PSDIV_200  str r2, [r1, #0x04]
#endif
#endif  /* CONFIG_S3C24X0 *//** we do sys-critical inits only at reboot,* not when booting from ram!*/
/*选择是否初始化CPU     */
#ifndef CONFIG_SKIP_LOWLEVEL_INITbl cpu_init_crit       /*协处理器配置(mmu、cache、sdram等初始化配置)*/
#endif/***************** CHECK_CODE_POSITION ******************************************/
/*
bl、b、adr指令的地址域是基于PC的相对偏移寻址,相当于PC+offset3 FLASH运行在FLASH运行时候,标号_start存储于0x00000000地址处,而相应的adr r0, _start命令存储于地址0x00000010处。当执行adr r0, _adr命令时,PC等于其存储地址0x00000010,因此r0 = PC + offset = 0x00000010 - 0x10 = 0x00000000。4 RAM运行假设已经通过重定位将U-Boot的代码复制到SDRAM 0x33f80000开始的位置,此时_start存储于0x33f80000地址处,adr r0, _ad命令存储于0x33f80010地址处。当在RAM中执行该命令时,PC等于其存储地址0x33f80010,偏移地址offset不变,因此r0 = PC + offset = 0x33f80010 - 0x10 = 0x33f80000。*/adr    r0, _start      /* r0 <- current position of code  读取_start在内存中的地址(通过相对偏移量来计算:pc-(pc与_start的地址差),pc值的两种可能在前面已经做说明)*/ldr    r1, _TEXT_BASE      /* test if we run from flash or RAM  起始链接地址*/cmp    r0, r1          /* don't reloc during debug    是否已经把代码拷贝到ram中运行  */beq stack_setup /*在ram中运行,则直接开始初始化堆栈*//***************** CHECK_CODE_POSITION ******************************************
不是NANDFLASH,就意味着是NORFLASH,那么就把所有代码拷贝到SDRAM,即跳到relocate
如果是NANDFLASH的话,那么就是NANDFLASH的初始化,然后也把所有代码拷贝到SDRAM原理:
norflash启动内存会同时映射到0地址和40000000地址,
因为前面往0x0000003C地址写了0xdeadbeef值,所以通过清空0x4000003C地址,
再判断0x0000003C地址是否也被清空,就可以知道是否是norflash启动。***************** CHECK_BOOT_FLASH ******************************************/
/*清零0x4000003C地址的内容*/ldr    r1, =( (4<<28)|(3<<4)|(3<<2) )       /* address of Internal SRAM  0x4000003C   */ mov    r0, #0      /* r0 = 0 */str    r0, [r1]/*读取0x0000003C地址中的内容*/mov   r1, #0x3c       /* address of men  0x0000003C*/ldr  r0, [r1]cmp r0, #0      /*0x0000003C地址中的数据是否等于0  (norflash启动时Z标志被置1,norflash启动时Z标志置零)*/bne relocate    /*Z标识位为0则,表示是norflash启动,跳转到relocate*//*-----------------------------------------------------------------------------------------------------------------------------
/*nandflash代码拷贝*/
判断结果是nandflash启动则往下执行,
把前面被清0的0x4000003C地址的内容(0xdeadbeef)写回去(借来用完了,给人家还回去)*//* recovery  */ldr  r0, =(0xdeadbeef)ldr   r1, =( (4<<28)|(3<<4)|(3<<2) )str    r0, [r1]/***************** CHECK_BOOT_FLASH ******************************************
从nand启动代码,定义u-boot在Nand flash中存放的长度为#define LENGTH_UBOOT 0x100000,
可以方便修改u-boot因为裁剪和增添大小的改变而占的长度。 首先检测代码位置,如果在内存中,
就设置堆栈,然后跳到c语言执行,否则就进行初始化nandflash,然后搬运代码***************** NAND_BOOT *************************************************/#define LENGTH_UBOOT 0x60000 /*uboot大小*/
#define NAND_CTL_BASE 0x4E000000    /*NAND Flash 相关寄存器起始地址 */#ifdef CONFIG_S3C2440
/* Offset */
#define oNFCONF 0x00        /*NFCONF寄存器地址偏移量(NAND Flash配置寄存器)*/
#define oNFCONT 0x04        /*NFCONT寄存器地址偏移量(NAND Flash控制寄存器) */
#define oNFCMD 0x08         /*NFCMMD寄存器地址偏移量(NAND Flash命令寄存器) */
#define oNFSTAT 0x20        /*NFSTAT寄存器地址偏移量(NAND Flash状态寄存器) */@ reset NAND/*NAND Flash   NFCONF寄存器的配置*/mov r1, #NAND_CTL_BASEldr   r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )str   r2, [r1, #oNFCONF]  /*把r2值写入NFCONF寄存器*/ldr  r2, [r1, #oNFCONF]  /*从NFCONF寄存器读值到r2*//*NAND Flash   NFCONT寄存器的配置*/ldr r2, =( (1<<4)|(0<<1)|(1<<0) )    @ Active low CE Control str    r2, [r1, #oNFCONT]ldr   r2, [r1, #oNFCONT]/*NAND Flash  NFSTAT寄存器的配置*/ldr   r2, =(0x6) @ RnB Clearstr r2, [r1, #oNFSTAT]ldr   r2, [r1, #oNFSTAT]/*NAND Flash   NFCMMD寄存器的配置*/mov  r2, #0xff   @ RESET commandstrb    r2, [r1, #oNFCMD]
//下面几句是一个延时程序用来等待几秒,等到NAND 准备好
/*加入延迟*/    mov r3, #0  @ wait
nand1: add  r3, r3, #0x1cmp r3, #0xablt nand1/*等到NAND 准备好*/
nand2:ldr   r2, [r1, #oNFSTAT]  @ wait readytst    r2, #0x4    //检查NFSTAT寄存器,是否准备好了beq nand2    //没有准备好则继续等待ldr    r2, [r1, #oNFCONT]orr   r2, r2, #0x2    @ Flash Memory Chip Disable//禁止掉NAND FLASH等到要使用FLASH时再使能str    r2, [r1, #oNFCONT]/*调用c函数去读取sp寄存器在任意时刻会保存我们栈顶的地址.fp寄存器属于通用寄存器,在某些时刻我们利用它保存栈底的地址!
*/  @ get read to call C functions (for nand_read())ldr    sp, DW_STACK_START  @ setup stack pointer  //每次需要从汇编调到C函数时 都需要设置好堆栈mov fp, #0  @ no previous frame, so fp=0   fp是R14/*
之前都是一些初始化,下面才开始利用C函数来真正开始拷贝.
从nandflash拷贝u-boot到ram*/@ copy U-Boot to RAMldr    r0, =TEXT_BASE /*传递第一个参数*/mov  r1, #0x0        /*传递第二个参数*/mov  r2, #LENGTH_UBOOT   /*传递第三个参数*/bl   nand_read_ll    /*调用c函数,函数会去获取上面三个参数*//*TST 类似于 CMP,不产生放置到目的寄存器中的结果。而是在给出的两个操作数上进行操作并把结果反映到状态标志上。
使用 TST 来检查是否设置了特定的位。操作数 1 是要测试的数据字而操作数 2 是一个位掩码。经过测试后,如果匹配则设置 Zero 标志,
否则清除它。象 CMP 那样,你不需要指定 S 后缀。 */   tst r0, #0x0    /*nand_read_ll函数返回值是否为0(第0位为0则Z标志被置1)*/beq    ok_nand_read  /*读nandflash结束*//*拷贝到nandflash失败*/
bad_nand_read:
loop2:b loop2   @ infinite loop
/*拷贝到nandflash成功*/
ok_nand_read:@ verifymov   r0, #0ldr   r1, =TEXT_BASEmov  r2, #0x400  @ 4 bytes * 1024 = 4K-bytes   //要比较多少个字节数go_next:ldr  r3, [r0], #4ldr r4, [r1], #4teq r3, r4bne   notmatch   /*比较失败(拷贝失败),进入死循环*/subs    r2, r2, #4beq   stack_setup /*比较完成,跳转到stack_setup执行栈初始化*/bne go_next  notmatch:
loop3:b loop3   @ infinite loop
#endif/*nandflash代码拷贝结束*/
/*-----------------------------------------------------------------------------------------------------------------------------#ifdef   CONFIG_S3C2410/* Offset */
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFSTAT 0x10@ reset NANDmov    r1, #NAND_CTL_BASEldr   r2, =0xf830    @ initial valuestr r2, [r1, #oNFCONF]ldr   r2, [r1, #oNFCONF]bic   r2, r2, #0x800  @ enable chipstr   r2, [r1, #oNFCONF]mov   r2, #0xff       @ RESET commandstrb    r2, [r1, #oNFCMD]mov    r3, #0  @ wait
nand1:add   r3, r3, #0x1cmp r3, #0xablt nand1nand2:ldr  r2, [r1, #oNFSTAT]  @ wait readytst    r2, #0x1beq nand2ldr    r2, [r1, #oNFCONF]orr   r2, r2, #0x800  @ disable chipstr  r2, [r1, #oNFCONF]@ get read to call C functions (for nand_read())ldr  sp, DW_STACK_START  @ setup stack pointermov   fp, #0  @ no previous frame, so fp=0@ copy U-Boot to RAMldr  r0, =TEXT_BASEmov  r1, #0x0mov r2, #LENGTH_UBOOTbl nand_read_lltst r0, #0x0beq ok_nand_readbad_nand_read:
loop2:b loop2   @ infinite loopok_nand_read:@ verifymov   r0, #0ldr   r1, =TEXT_BASEmov  r2, #0x400  @ 4 bytes * 1024 = 4K-bytes
go_next:ldr r3, [r0], #4ldr r4, [r1], #4teq r3, r4bne   notmatchsubs    r2, r2, #4beq   stack_setupbne  go_nextnotmatch:
loop3:b loop3   @ infinite loop#endif
/***************** NAND_BOOT *************************************************//***************** NOR_BOOT *************************************************/
/**norflash启动代码复制 */relocate:               /* relocate U-Boot to RAM       *//*********** CHECK_FOR_MAGIC_NUMBER***************/ldr    r1, =(0xdeadbeef)  /**0x0000003c地址重新赋值0xdeadbeef */cmp r0, r1          /** */bne   loop3   /**进入死循环 *//*********** CHECK_FOR_MAGIC_NUMBER***************/adr   r0, _start      /* r0 <- current position of code   */ldr    r1, _TEXT_BASE      /* test if we run from flash or RAM */ldr   r2, _armboot_startldr   r3, _bss_startsub   r2, r3, r2      /* r2 <- size of armboot            */add    r2, r0, r2      /* r2 <- source end address         */copy_loop:ldmia    r0!, {r3-r10}       /* copy from source address [r0]    */stmia r1!, {r3-r10}       /* copy to   target address [r1]    */cmp   r0, r2          /* until source end addreee [r2]    */ble   copy_loop
/***************** NOR_BOOT *************************************************/
/* 初始化堆栈        *//* 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_IRQsub    r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endifsub   sp, r0, #12     /* leave 3 words for abort-stack    */
/*得到最终sp的值*/
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, #4cmp   r0, r1ble   clbss_lldr  pc, _start_armboot
/**********************************************************************/
* 已经准备好了堆栈,就可跳到C写的代码里了,也就是
* 跳到内存中的/u-boot-1.1.4/board.c --> start_armboot中运行了
* 把_start_armboot地址处的值也就是start_armboot绝对地址值移到pc
* 于是跳到C代码。start_armboot是U-Boot执行的第一个C语言函数,完成如下工作:
1. 初始化MMU
2.初始化外部端口
3. 中断处理程序表初始化
4. 串口初始化
5. 其它部分初始化(可选)
6. 主程序循环
/*********************************************************************/
#if defined(CONFIG_MINI2440_LED)
#define GPIO_CTL_BASE 0x56000000
#define oGPIO_B 0x10
#define oGPIO_CON 0x0
/* R/W, Configures the pins of the port */
#define oGPIO_DAT 0x4
#define oGPIO_UP 0x8
/* R/W, Pull-up disable register */mov  r1, #GPIO_CTL_BASEadd   r1, r1, #oGPIO_Bldr r2, =0x295551str   r2, [r1, #oGPIO_CON]mov r2, #0xffstr    r2, [r1, #oGPIO_UP]ldr  r2, =0x1c1str  r2, [r1, #oGPIO_DAT]
#endif_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* 初始化CACHES*/mov   r0, #0mcr   p15, 0, r0, c7, c7, 0   /* flush v3/v4 cache *//*使I/D cache失效: 协处理寄存器操作,将r0中的数据写入到协处理器p15的c7中,c7对应cp15的cache控制寄存器*/mcr p15, 0, r0, c8, c7, 0   /* flush v4 TLB *//*使TLB操作寄存器失效:将r0数据送到cp15的c8、c7中。C8对应TLB操作寄存器*//** disable MMU stuff and caches* 关闭MMU和CACHES*/mrc p15, 0, r0, c1, c0, 0   //将c1、c0的值写入到r0中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) Alignorr   r0, r0, #0x00001000 @ set bit 12 (I) I-Cachemcr    p15, 0, r0, c1, c0, 0   //将设置好的r0值写入到协处理器p15的c1、c0中@对协处理器的操作请查看《ARM技术手册(ARM920T Technical Reference Manual)》的协处理器部分。/** 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.*/@初始化RAM时钟,因为内存是跟开发板密切相关的,所以这部分在/开发板目录/lowlevel_init.S中实现 mov ip, lr   /*ip是R12的别名,这里是保存当前lr,不然lr的值会被下面的bl  lowlevel_init跳转给覆盖掉*/bl lowlevel_init   /*在/board/samsung/smdk2410/lowlevel_init.S中,对SDRAM内存时钟相关的设置*/mov    lr, ip   /*还原lr*/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*/@bad_save_user_regs 内联(注意:.macro表示编译时插入到调用位置,非调用)代码段,用来保存程序“出错”时用户态的寄存器的值.macro  bad_save_user_regssub   sp, sp, #S_FRAME_SIZE   @在栈中预留 S_FRAME_SIZE字节(72)的空间,用来保存当前(72/4=18个)寄存器的值@之所以先保存r0-r12 ,后面会被改变stmia   sp, {r0 - r12}          @ Calling r0-r12   @保存r0 - r12到栈中 (stmia递增存储,存储完后sp重新指向原先栈地址)ldr r2, _armboot_start  sub r2, r2, #(CONFIG_STACKSIZE)sub  r2, r2, #(CONFIG_SYS_MALLOC_LEN)/* set base 2 words into abort stack */sub  r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8)   @CFG_GBL_DATA_SIZE是一个全局变量的大小,8:两个字,用来存放发生这些异常时的pc、cpsr寄存器    ldmia   r2, {r2 - r3}           @ get pc, cpsr     @把发生异常时,使用get_bad_stack保存的pc、cpsr寄存器加载到r2,r3add r0, sp, #S_FRAME_SIZE       @ restore sp_SVC    @ r0=原来的SP值,就是sp_SVC,即被中断时的SP值add  r5, sp, #S_SP       @ S_SP等于52,表示“原来SP + 52”的地方,是保存sp寄存器mov r1, lr              @ r1=lr    被中断时的lr值@保存sp_SVC, lr_SVC, pc, cpsr 到栈中stmia  r5, {r0 - r3}           @ save sp_SVC, lr_SVC, pc, cpsr   mov  r0, sp      @r0=sp.endm@irq_save_user_regs代码段,用来保存程序“出错”时用户态的寄存器的值.macro irq_save_user_regssub   sp, sp, #S_FRAME_SIZE   @在栈中预留 S_FRAME_SIZE字节(72)的空间,用来保存当前(72/4=18个)寄存器的值stmia    sp, {r0 - r12}          @ Calling r0-r12   @保存r0 - r12到栈中 (stmia递增存储,存储完后sp重新指向原先栈地址)add r7, sp, #S_PCstmdb  r7, {sp, lr}^           @ Calling SP, LRstr    lr, [r7, #0]            @ Save calling PCmrs   r6, spsrstr r6, [r7, #4]            @ Save CPSRstr r0, [r7, #8]            @ Save OLD_R0mov   r0, sp.endm.macro   irq_restore_user_regsldmia  sp, {r0 - lr}^          @ Calling r0 - lrmov   r0, r0ldr   lr, [sp, #S_PC]         @ Get PCadd    sp, sp, #S_FRAME_SIZE/* return & move spsr_svc into cpsr */subs pc, lr, #4.endm
@保存lr,spsr.macro get_bad_stackldr   r13, _armboot_start     @ setup our mode stacksub  r13, r13, #(CONFIG_STACKSIZE)sub    r13, r13, #(CONFIG_SYS_MALLOC_LEN)/* reserve a couple spots in abort stack */sub    r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8)    @CFG_GBL_DATA_SIZE是一个全局变量的大小,8:两个字,用来存放发生这些异常时的pc、cpsr寄存器@把lr寄存器保存到r13这个地址中  (当前r13指向     _armboot_start-CONFIG_STACKSIZE-CONFIG_SYS_MALLOC_LEN-CONFIG_SYS_GBL_DATA_SIZE-8这个地址)str lr, [r13]           @ save caller lr / spsr    @读spsr寄存器,并且把它保存到      _armboot_start-CONFIG_STACKSIZE-CONFIG_SYS_MALLOC_LEN-CONFIG_SYS_GBL_DATA_SIZE-4这个地址mrs  lr, spsrstr lr, [r13, #4]@CPU切换回到SVC模式(发生中断模式被改变了)mov    r13, #MODE_SVC          @ prepare SVC-Mode@ msr   spsr_c, r13msr  spsr, r13mov    lr, pcmovs  pc, lr  @返回.endm.macro get_irq_stack           @ setup IRQ stackldr   sp, IRQ_STACK_START.endm.macro get_fiq_stack            @ setup FIQ stackldr   sp, FIQ_STACK_START.endm/** exception handlers*/.align  5
undefined_instruction:get_bad_stack @保存寄存器pc和cpsr到内存中bad_save_user_regs        @分配栈空间,保存r0-r12寄存器到栈中,读取前面保存的pc和cpsr,并保存到栈中bl do_undefined_instruction    @调用c函数,打印前面保存在栈中的18个寄存器的值.align 5
software_interrupt:get_bad_stackbad_save_user_regsbl    do_software_interrupt.align 5
prefetch_abort:get_bad_stackbad_save_user_regsbl    do_prefetch_abort.align 5
data_abort:get_bad_stackbad_save_user_regsbl    do_data_abort.align 5
not_used:get_bad_stackbad_save_user_regsbl  do_not_used#ifdef CONFIG_USE_IRQ.align  5
irq:
//Apollo +
/*get_irq_stackirq_save_user_regsbl do_irqirq_restore_user_regs
*//* use IRQ for USB and DMA */sub    lr, lr, #4             @ the return addressldr    sp, IRQ_STACK_START     @ the stack for irqstmdb   sp!,  { r0-r12,lr }     @ save registersldr    lr, =int_return         @ set the return addrldr    pc, =IRQ_Handle         @ call the isr
int_return:ldmia  sp!, { r0-r12,pc }^    @ return from interrupt
//Apollo -.align    5
fiq:get_fiq_stack/* someone ought to write a more effiction fiq_save_user_regs */irq_save_user_regsbl   do_fiqirq_restore_user_regs#else.align  5
irq:get_bad_stackbad_save_user_regsbl   do_irq.align    5
fiq:get_bad_stackbad_save_user_regsbl   do_fiq#endif

u-boot 启动代码start.S详解(for mini2440)相关推荐

  1. u-boot启动代码start.S详解360

    (1)定义入口.由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本 ...

  2. [PXE] Linux(centos6)中PXE 服务器搭建,PXE安装、启动及PXE理论详解

    本篇blog主要讲述了[PXE] linux(centos)PXE无盘服务器搭建,安装,启动及pxe协议详解 , Kickstart (PXE+DHCP+TFTP+HTTP). PXE环境概述 作为中 ...

  3. Linux_arm_启动_c语言部分详解,[原创]Linux arm 启动 c语言部分详解第四讲

    Linux arm启动c语言部分详解第四讲(from setup_per_cpu_areas();) Written by leeming 上面的setup_arch花了我们大量的篇幅,现在我们要继续 ...

  4. Spring Boot 2.0 的配置详解(图文教程)

    本文来自作者 泥瓦匠 @ bysocket.com 在 GitChat 上分享 「Spring Boot 2.0 的配置详解(图文教程)」 编辑 | 哈比 Spring Boot 配置,包括自动配置和 ...

  5. 怎么打开python shell_Python之使用adb shell命令启动应用的方法详解

    一直有一个心愿希望可以用Python做安卓自动化功能测试,在一步步摸索中,之前是用monkeyrunner,但是发现对于控件ID的使用非常具有局限性,尤其是ID的内容不便于区分 具有重复性时,后面又发 ...

  6. python调用adb shell命令_Python之使用adb shell命令启动应用的方法详解

    Python之使用adb shell命令启动应用的方法详解 一直有一个心愿希望可以用Python做安卓自动化功能测试,在一步步摸索中,之前是用monkeyrunner,但是发现对于控件ID的使用非常具 ...

  7. Tomcat 项目代码上线步骤详解

    Tomcat 项目代码上线步骤详解 1.上线内容(JSP代码,图片,包文件(jar|war|ear)) 2.上线内容来源 a.开发人员提供(邮件形式).tar.gz 压缩包(包文件开发负责编译 编译命 ...

  8. spring boot(四):thymeleaf使用详解

    spring boot(四):thymeleaf使用详解 在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用. ...

  9. Spring Boot 使用 Druid 连接池详解

    Spring Boot 使用 Druid 连接池详解 Alibaba Druid 是一个 JDBC 组件库,包含数据库连接池.SQL Parser 等组件,被大量业务和技术产品使用或集成,经历过严苛的 ...

最新文章

  1. 深度学习之反向传播算法
  2. 什么时候用activity什么时候用fragment
  3. Mac安装PhoneGap3
  4. 【Linux学习】Linux系统管理2—作业调度
  5. 6700设置上网教程
  6. nyoj-976-Youth的最大化(贪心+二分)
  7. Django 如何实现 如下 联表 JOIN 查询?
  8. 1.3.3 错题整理(组成原理)
  9. 【LeetCode】LeetCode之跳跃游戏——动态规划+贪心算法
  10. 校园导游java版,校园导游系统Word版
  11. android mqtt详解_Android mqtt入门 Android studio(转)
  12. 怎样对流媒体进行压力测试_四合一气体检测仪怎样进行气体测试?
  13. 未名 - markdown
  14. Struts2 面试题
  15. Qt_屏幕保护程序、进程监听、数据库读取、屏幕保护
  16. 小学计算机课教师教学笔记,小学信息技术教师教育随笔
  17. unity暂停和继续按钮
  18. 什么是link标签?
  19. k8s学习-网络策略NetworkPolicy(概念、模版、创建、删除等)
  20. 查看自己本地IP地址方法

热门文章

  1. 学计算机专业简历英语,计算机专业的英文简历.docx
  2. win10 隐藏盘符
  3. nginx 验证配置文件
  4. 解决Mac电脑下Sublime Text3快捷键html:5+Tab没有反应
  5. windows10如何强制杀进程
  6. Dubbo(超级无敌认真好用,万字收藏篇!!!!)
  7. JavaWeb第四天
  8. 阿里巴巴面试经2021
  9. 小结生活,大结工作问题
  10. Samba服务器搭建(基础篇)