U-BOOT启动流程分析--start.s(二)
一、概述 u-boot的启动流程: uboot-第一阶段硬件初始化主要实现了: 二、细节分析(参考一、参考二) ? 1、建立异常向量表 #include <config.h> ??//实际上定位的头文件是include/configs/smdk2410.h ?在第1行中".globl _start":使用.globol声明全局符号_start,在 board/100ask24x0/u-boot.lds中ENTRY(_start)这里用到 33f80000 t $a 33f80000 T _start //_start符号被链接在33f80000,其中33f80000是生成bin文件的运行启始地址. 33f80020 t $d 33f80020 t _undefined_instruction //_undefined_instruction符号被链接在33f80020 ... 33f80160 t undefined_instruction //_undefined_instruction指向的undefined_instruction符号被链接在33f80160 33f801c0 t software_interrupt 33f80220 t prefetch_abort 33f80280 t data_abort 33f802e0 t not_used 33f80340 T Launch 33f803b0 t On_Steppingstone 33f80400 t irq ... 在第2行中_start之所以有8种不同的异常处理,是在2440芯片手册中已经规定好了的,如下图1: CPU一上电设置了入口地址"ENTRY(_start)"后,就会进入"_start"全局符号中执行上面第3行跳转到复位异常字段: "b reset"。由上,复位异常处理需要进入管理模式(0x0000 0000),所以"b reset"跳转到了设置管理模式。 问题1:后面的异常处理为什么用ldr 不用b指令? ldr是绝对跳转,b是相对跳转,它的地址与代码位置无关。复位异常在CPU运行前,并未初始化SDRAM(故不能使用0x3000 0000以上的地址),在正常工作后也可能触发复位,这时由于CPU已经对SDRAM、MMU(虚拟内存管理)等初始化了, 2.后面的异常处理是怎么执行的?执行后异常处理又怎么退出的? 在2440芯片手册上给出,例如当处理一个中断IRQ异常时: d.强制 PC 从相关异常向量处取下条指令。跳转到0x18实现中断异常处理. ?b). 复制 SPSR 的内容返回给 CPSR 中。 ? 问题3. 第12行中?.word: ? ? ? ?类似于(unsigend long) ? ? ? 以第12行中?_undefined_instruction: .word undefined_instruction为例 用C语言表示就是:_undefined_instruction = &undefined_instruction 相当于PC从_undefined_instruction取值时,即undefined_instruction地址存到了PC中。 ? 2、设置管理模式、关看门狗、屏蔽中断 reset--->start.o reset: /* 设置CPSR程序程序状态寄存器为管理模式 */ mrs r0,cpsr //MRS读出CPSR寄存器值到R0 bic r0,r0,#0x1f //将R0低5位清空 orr r0,#0xd3 //R0与b‘110 10011按位或,禁止IRQ和FIQ中断,10011:复位需要设为管理模式(图1) msr cpsr,r0 //MSR写入CPSR寄存器 /* 关看门狗 */ # define pWTCON 0x53000000 //(WitchDog Timer)看门狗定时器寄存器WTCON,设为0X0表示关闭看门狗 # define INTMOD 0X4A000004 //(Interrupt Mode)中断模式寄存器INTMOD,相应位=0:IRQ模式,相应位=1:IRQ模式, # define INTMSK 0x4A000008 //(Interrupt Mask)中断屏蔽寄存器INTMSK,相应位=0:开启中断服务,相应位=1:关闭中断服务 # define INTSUBMSK 0x4A00001C //中断次级屏蔽寄存器,相应位=0:开启中断服务,相应位=1:关闭中断服务 # define CLKDIVN 0x4C000014 //时钟分频寄存器 #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) //宏定义CONFIG_S3C2410已定义 ldr r0,=pWTCON //R0等于WTCON地址 mov r1,#0x0 //R1=0x0 str r1,[r0] //关闭WTCON寄存器,pWTCON=0; /* 关中断 */ mov r1,#0xffffffff //R1=0XFFFF FFFF ldr r0,=INTMSK //R0等于INTMSK地址 str r1,[r0] //*0x4A000008=0XFFFF FFFF(关闭所有中断) # if defined(CONFIG_S3C2410) ldr r1,=0x3ff //R1=0x3FF ldr r0,=INTSUBMSK //R0等于INTSUBMSK地址 str r1,[r0] //*0x4A00001C=0x3FF(关闭次级所有中断) # endif /* 判断系统是从nand启动的还是直接将程序下载到SDRAM中运行, 若系统从nand启动,这里得到r0和r1值是不一样的,r1=0x33f80000, 而r0=0x00000000。说明没初始化SDRAM,ne(no equal)标识符为真,所以bl cpu_init_crit执行跳转. */ adr r0,_start ldr r1,_TEXT_BASE cmp r0,r1 blne cpu_init_crit CPU复位后是从这里开始执行,这里初始化了: 3、进入cpu_init_crit函数(关闭MMU): cpu_init_crit: mov r0,#0 mcr p15,0,c7,0 //关闭ICaches(指令缓存,关闭是为了降低MMU查表带来的开销)和DCaches(数据缓存,DCaches使用的是虚拟地址,开启MMU之前必须关闭) mcr p15,0,c8,0 //使无效整个数据TLB和指令TLB(TLB就是负责将虚拟内存地址翻译成实际的物理内存地址) mrc p15,c1,c0,0 bic r0,#0x00002300 @ clear bits 13,9:8 (--V- --RS) //bit8:系统不保护,bit9:ROM不保护,bit13:设置正常异常模式0x0~0x1c,即异常模式基地址为0X0 bic r0,#0x00000087 @ clear bits 7,2:0 (B--- -CAM) //bit0~2:禁止MMU,禁止地址对齐检查,禁止数据Cache.bit7:设为小端模式 orr r0,#0x00000002 @ set bit 2 (A) Align //bit2:开启数据Cache orr r0,#0x00001000 @ set bit 12 (I) I-Cache //bit12:开启指令Cache mcr p15,0 /* mcr/mrc: Caches:是一种高速缓存存储器,用于保存CPU频繁使用的数据。在使用Cache技术的处理器上,当一条指令要访问内存的数据时, 首先查询cache缓存中是否有数据以及数据是否过期,如果数据未过期则从cache读出数据。处理器会定期回写cache中的数据到内存。 根据程序的局部性原理,使用cache后可以大大加快处理器访问内存数据的速度。 其中DCaches和ICaches分别用来存放数据和执行这些数据的指令 TLB:就是负责将虚拟内存地址翻译成实际的物理内存地址,TLB中存放了一些页表文件,文件中记录了虚拟地址和物理地址的映射关系。 当应用程序访问一个虚拟地址的时候,会从TLB中查询出对应的物理地址,然后访问物理地址。TLB通常是一个分层结构, 使用与Cache类似的原理。处理器使用一定的算法把最常用的页表放在最先访问的层次。 这里禁用MMU,是方便后面直接使用物理地址来设置控制寄存器 */ mov ip,lr //临时保存当前子程序返回地址,因为接下来执行bl会覆盖当前返回地址. bl lowlevel_init //跳转到lowlevel_init(位于u-boot-1.1.6/board/100ask24x0/lowlevel_init.S) mov lr,ip //恢复当前返回地址 mov pc,lr //退出 ? 4、进入lowlevel_init函数 (初始化各个bank和SDRAM)、 lowlevel_init: ldr r0,=SMRDATA //将SMRDATA的首地址(0x33F806C8)存到r0中 ldr r1,_TEXT_BASE //r1等于_TEXT_BASE内容,也就是TEXT_BASE(0x33F80000) sub r0,r1 //将0x33F806C8与0x33F80000相减,得到现在13个寄存器值在NOR Flash上存放的开始地址 ldr r1,=BWSCON //将BWSCON寄存器地址值存到r1中 (第一个存储器寄存器首地址) add r2,#13*4 //每个寄存器4字节,r2=r0+13*4=NOR Flash上13个寄存器值最后一个地址 0: ldr r3,[r0],#4 //将r0的内容存到r3的内容中(r3等于SMRDATA里面值), 同时r0地址+=4; str r3,[r1],#4 //将r3的内容存到r1所指的地址中(向寄存器地址里写入r3值),同时r1地址+=4; cmp r2,r0 // 判断r2和r0 bne 0b //不等则跳转到第6行继续执行 mov pc,lr //跳回到返回地址中继续执行 SMRDATA: .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) //设置每个BWSCON,注意BANK0由硬件连线决定了 .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)) //设置BANKCON0~BANKCON5 .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) //设置BANKCON6~BANKCON7 .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) //设置REFRESH,在S3C2440中11~17位是保留的,也即(Tchr<<16)无意义 .word 0xb1 //设置BANKSIZE,对于容量可以设置大写,多出来的空内存会被自动检测出来 .word 0x30 //设置MRSRB6 .word 0x30 //设置MRSRB7 ? 5、返回start.s -- 设置栈 stack_setup: //设置栈,方便调用C函数 ldr r0,_TEXT_BASE //代码段的初始地址:r0=0x33f80000 sub r0,#CFG_MALLOC_LEN //留出一段内存以实现malloc:r0=0x33f50000 sub r0,#CFG_GBL_DATA_SIZE //再留出一段存一些全局参数的变量:r0=0x33F4FF80 #ifdef CONFIG_USE_IRQ sub r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) //中断与快中断的栈:r0=0x33F4DF7C #endif sub sp,#12 //留出12字节内存给abort异常 设置栈顶sp=r0-12; #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl clock_init //进入clock_init函数 #endif ? 6、进入clock_init函数设置时钟 void clock_init(void) { S3C24X0_CLOCK_POWER *clk_power = (S3C24X0_CLOCK_POWER *)0x4C000000; //定义一个S3C24X0_CLOCK_POWER型结构体指针,clk_power->LOCKTIME=0x4C000000 if (isS3C2410) //isS3C2410为0,执行else {... ...} else { /* FCLK:HCLK:PCLK = 1:4:8 */ clk_power->CLKDIVN = S3C2440_CLKDIV; //S3C2440_CLKDIV=0X05 /* change to asynchronous bus mod */ __asm__( "mrc p15,r1,0n" /* read ctrl register */ "orr r1,#0xc0000000n" //使其从快总线模式改变为异步总线模式,在2440手册上看到 "mcr p15,0n" /* write ctrl register */ :::"r1" //:::"r1" 向GCC声明:我对r1作了改动 ); /* to reduce PLL lock time,adjust the LOCKTIME register */ clk_power->LOCKTIME = 0xFFFFFFFF; //PLL 锁定时间计数寄存器 /* configure UPLL */ clk_power->UPLLCON = S3C2440_UPLL_48MHZ; //UCLK=48Mhz /* some delay between MPLL and UPLL */ delay (4000); //等待UCLK时钟波形稳定 /* configure MPLL */ clk_power->MPLLCON = S3C2440_MPLL_400MHZ; //FCLK=400Mhz /* some delay between MPLL and UPLL */ delay (8000); //等待FCLK时钟波形稳定 } } ? 7、重定位、清bss段、跳转到start_armboot函数 relocate: /* 拷贝u-boot到SDRAM */ adr r0,_start //r0:当前代码开始地址 ldr r1,_TEXT_BASE //r1:代码段连接地址(0X3FF8 0000) cmp r0,r1 //测试现在在FLASH中还是RAM中 beq clear_bss //若_start==_TEXT_BASE,表示已经进行代码从Flash拷贝SDRAM了(通常是调试时直接下载到RAM中) ldr r2,_armboot_start //r2等于_armboot_start里的内容,也就是_start ldr r3,_bss_start //r3等于_bss_start里的内容,(在连接脚本u-boot.lds中定义,是代码段的结束地址) sub r2,r3,r2 //r2等于代码段长度 bl CopyCode2Ram // r0: source,r1: dest,r2: size 将从NOR FLASH上代码段(r0~r0+r2)拷贝到sdram地址(r3)0x3ff80000代码段地址上 ? 小结: uboot-第一阶段硬件初始化主要实现了: (主要摘自第1阶段——uboot分析之硬件初始化start.S(4)) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |