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

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, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr, r0

@	bl	coloured_LED_init
@	bl	red_LED_on

@针对AT91RM9200进行特殊处理
#if	defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
	/*
	 * 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

@针对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, =pWTCON
	mov	r1, #0x0
	str	r1, [r0]
	
@关中断
	/*
	 * mask all IRQs by setting all bits in the INTMR - default
	 */
	mov	r1, #0xffffffff
	ldr	r0, =INTMSK
	str	r1, [r0]
@关次中断	
# if defined(CONFIG_S3C2410)
	ldr	r1, =0x7ff
	ldr	r0, =INTSUBMSK
	str	r1, [r0]
# endif

#if defined(CONFIG_S3C2440)
	ldr	r1, =0x7fff	
	ldr	r0, =INTSUBMSK
	str	r1, [r0]
#endif


#if defined(CONFIG_S3C2440)
/*配置FCLK:HCLK:PCLK的分频比*/
	/* FCLK:HCLK:PCLK = 1:4:8 */
	ldr	r0, =CLKDIVN
	mov	r1, #5
	str	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, =CLKDIVN
	mov	r1, #3
	str	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_INIT
	bl	cpu_init_crit		/*协处理器配置(mmu、cache、sdram等初始化配置)*/
#endif



/***************** CHECK_CODE_POSITION ******************************************/
/*
bl、b、adr指令的地址域是基于PC的相对偏移寻址,相当于PC+offset

3 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启动则往下执行,
把前面被清00x4000003C地址的内容(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_BASE
	ldr	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 Clear
	str	r2, [r1, #oNFSTAT]
	ldr	r2, [r1, #oNFSTAT]
	/*NAND Flash   NFCMMD寄存器的配置*/
	mov	r2, #0xff	@ RESET command
	strb	r2, [r1, #oNFCMD]
//下面几句是一个延时程序用来等待几秒,等到NAND 准备好
/*加入延迟*/	
	mov	r3, #0	@ wait
nand1: 
	add	r3, r3, #0x1
	cmp	r3, #0xa
	blt	nand1
	
/*等到NAND 准备好*/
nand2:
	ldr	r2, [r1, #oNFSTAT]	@ wait ready
	tst	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 RAM
	ldr	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:
	@ verify
	mov	r0, #0
	ldr	r1, =TEXT_BASE
	mov	r2, #0x400	@ 4 bytes * 1024 = 4K-bytes	//要比较多少个字节数
	
go_next:
	ldr	r3, [r0], #4
	ldr	r4, [r1], #4
	teq	r3, r4
	bne	notmatch   /*比较失败(拷贝失败),进入死循环*/
	subs	r2, r2, #4
	beq	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 NAND
	mov	r1, #NAND_CTL_BASE
	ldr	r2, =0xf830	@ initial value
	str	r2, [r1, #oNFCONF]
	ldr	r2, [r1, #oNFCONF]
	bic	r2, r2, #0x800	@ enable chip
	str	r2, [r1, #oNFCONF]
	mov	r2, #0xff		@ RESET command
	strb	r2, [r1, #oNFCMD]
	
	
	mov	r3, #0	@ wait
nand1:
	add	r3, r3, #0x1
	cmp	r3, #0xa
	blt	nand1

nand2:
	ldr	r2, [r1, #oNFSTAT]	@ wait ready
	tst	r2, #0x1
	beq	nand2
	
	ldr	r2, [r1, #oNFCONF]
	orr	r2, r2, #0x800	@ disable chip
	str	r2, [r1, #oNFCONF]
	
	@ get read to call C functions (for nand_read())
	ldr	sp, DW_STACK_START	@ setup stack pointer
	mov	fp, #0	@ no previous frame, so fp=0
	
	@ copy U-Boot to RAM
	ldr	r0, =TEXT_BASE
	mov	r1, #0x0
	mov	r2, #LENGTH_UBOOT
	bl	nand_read_ll
	tst	r0, #0x0
	beq	ok_nand_read

bad_nand_read:
loop2:
	b	loop2	@ infinite loop


ok_nand_read:
	@ verify
	mov	r0, #0
	ldr	r1, =TEXT_BASE
	mov	r2, #0x400	@ 4 bytes * 1024 = 4K-bytes
go_next:
	ldr	r3, [r0], #4
	ldr	r4, [r1], #4
	teq	r3, r4
	bne	notmatch
	subs	r2, r2, #4
	beq	stack_setup
	bne	go_next

notmatch:
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_start
	ldr	r3, _bss_start
	sub	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_IRQ
	sub	r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
	sub	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, #4
	cmp	r0, r1
	ble	clbss_l

	ldr	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_BASE
	add	r1, r1, #oGPIO_B
	ldr	r2, =0x295551
	str	r2, [r1, #oGPIO_CON]
	mov	r2, #0xff
	str	r2, [r1, #oGPIO_UP]
	ldr	r2, =0x1c1
	str	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, #0
	mcr	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) Align
	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-Cache
	mcr	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_regs
	sub	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,r3
	add	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_regs
	sub	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_PC
	stmdb	r7, {sp, lr}^			@ Calling SP, LR
	str	lr, [r7, #0]			@ Save calling PC
	mrs	r6, spsr
	str	r6, [r7, #4]			@ Save CPSR
	str	r0, [r7, #8]			@ Save OLD_R0
	mov	r0, sp
	.endm

	.macro	irq_restore_user_regs
	ldmia	sp, {r0 - lr}^			@ Calling r0 - lr
	mov	r0, r0
	ldr	lr, [sp, #S_PC]			@ Get PC
	add	sp, sp, #S_FRAME_SIZE
	/* return & move spsr_svc into cpsr */
	subs	pc, lr, #4
	.endm
@保存lr,spsr
	.macro get_bad_stack
	ldr	r13, _armboot_start		@ setup our mode stack
	sub	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, spsr
	str	lr, [r13, #4]
	@CPU切换回到SVC模式(发生中断模式被改变了)
	mov	r13, #MODE_SVC			@ prepare SVC-Mode
	@ msr	spsr_c, r13
	msr	spsr, r13
	mov	lr, pc
	movs	pc, lr	@返回
	.endm

	.macro get_irq_stack			@ setup IRQ stack
	ldr	sp, IRQ_STACK_START
	.endm

	.macro get_fiq_stack			@ setup FIQ stack
	ldr	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_stack
	bad_save_user_regs
	bl	do_software_interrupt

	.align	5
prefetch_abort:
	get_bad_stack
	bad_save_user_regs
	bl	do_prefetch_abort

	.align	5
data_abort:
	get_bad_stack
	bad_save_user_regs
	bl	do_data_abort

	.align	5
not_used:
	get_bad_stack
	bad_save_user_regs
	bl	do_not_used

#ifdef CONFIG_USE_IRQ

	.align	5
irq:
//Apollo +
/*
	get_irq_stack
	irq_save_user_regs
	bl	do_irq
	irq_restore_user_regs
*/
    /* use IRQ for USB and DMA */
        sub    lr, lr, #4             @ the return address
        ldr    sp, IRQ_STACK_START     @ the stack for irq
       stmdb   sp!,  { r0-r12,lr }     @ save registers
        
        ldr    lr, =int_return         @ set the return addr
        ldr    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_regs
	bl	do_fiq
	irq_restore_user_regs

#else

	.align	5
irq:
	get_bad_stack
	bad_save_user_regs
	bl	do_irq

	.align	5
fiq:
	get_bad_stack
	bad_save_user_regs
	bl	do_fiq

#endif
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yfw&武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值