第1阶段——uboot分析之硬件初始化start.S(4)
分析uboot第一个执行函数_start(cpu/arm920t/start.S)
打开cpu/arm920t/start.S
.globl _start // .globl定义一个全局符号"_start",表明_start这个符号要被链接器用到
_start: //_start:系统复位设置,以下共8种不同的异常处理
b reset //复位异常 0x0
ldr pc, _undefined_instruction //未定义的指令异常 0x4
ldr pc, _software_interrupt // 软件中断异常 0x8
ldr pc, _prefetch_abort //内存操作异常 0xc
ldr pc, _data_abort //数据异常 0x10
ldr pc, _not_used //未使用 0x14
ldr pc, _irq //中断IRQ异常 0x18
ldr pc, _fiq //快速中断FIQ异常 0x1c _undefined_instruction: .word undefined_instruction //0x20
_software_interrupt: .word software_interrupt //0x24
_prefetch_abort: .word prefetch_abort // 0x28
_data_abort: .word data_abort //0x2c
_not_used: .word not_used //0x30
_irq: .word irq //0x34
_fiq: .word fiq //0x38 .balignl ,0xdeadbeef //0x3c
在第1行中".globl _start":使用.globol声明全局符号_start,在 board/100ask24x0/u-boot.lds中ENTRY(_start)这里用到
其中符号保存的地址都在顶层目录/system.map中列出来了
system.map文件开头部分如下:
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:
图1
从上图可以看出复位异常处理需要进入管理模式(0X00000000),所以start.S 中“b reset”跳转到设置管理模式。
在linux中的异常向量地址是经过MMU(虚拟内存管理)产生的虚拟地址,比如中断地址:
0x18映射到物理地址是0xc000 0018(映射地址由自己设定),所以linux把中断向量放在0xc000 0018就行了。
CPU一上电设置了入口地址"ENTRY(_start)"后,就会进入"_start"全局符号中执行上面第3行跳转到复位异常字段: "b reset".
1.那么后面的异常处理为什么用ldr不用b指令?
之所以第一句使用b reset,是因为ldr指令属于绝对跳转,而b属于相对跳转,它的地址与代码位置无关。
因为复位异常在CPU运行前是没有初始化SDRAM(不能使用0X30000000以上地址),
在正常工作后也可能触发复位,这时由于CPU已经对SDRAM、MMU(虚拟内存管理)等初始化了,
此时的虚拟地址和物理地址完全不同,所以reset使用b指令相对跳转。
2.后面的异常处理是怎么执行的?执行后异常处理又怎么退出的?
在2440芯片手册上给出,例如当处理一个中断IRQ异常时:
a.保存当前PC现场到寄存器R14,
b.把当前程序状态寄存器(CPSR)保存到备份程序状态寄存器(SPSR)中.从异常退出的时候,就可以由SPSR来恢复CPSR。
c.根据中断IRQ异常处理,强制将 CPSR 模式位设为中断模式,如下图
d.强制 PC 从相关异常向量处取下条指令。跳转到0x18实现中断异常处理.
当退出中断IRQ异常时:
a. 将中断IRQ所对应的是R14_irq寄存器并放入到 PC 中,如下图,中断IRQ所对应的是R14_irq寄存器,执行MOVS R14_svc .
b. 复制 SPSR 的内容返回给 CPSR 中。
c. 如果在异常进入时置位了中断禁止标志位异常,清除中断禁止标志位
3. 第12行中 .word: 类似于(unsigend long)
以第12行中 _undefined_instruction: .word undefined_instruction为例
_undefined_instruction和undefined_instruction都是一个标号,
表示_undefined_instruction指向一个32位(4字节)地址,该地址用undefined_instruction符号变量代替。
用C语言表示就是:_undefined_instruction = &undefined_instruction
相当于PC从_undefined_instruction取值时,即undefined_instruction地址存到了PC中。
4.第20行中 .balignl 16,0xdeadbeef:
它的意思就是在以当前地址开始,在地址为16的倍数的指令位置的上一个指令填入为0xdeadbeef的内容。
此时当前地址刚好0x3c=60,由于ARM每个指令间隔4个字节,且64%16=0,所以在0x3c中填入0xdeadbeef。
它们的作用就是为内存做标记,插在那里,这个位置往前有特殊作用的内存,禁止访问。
接下来继续往下看start.o
reset:
/* 设置CPSR程序程序状态寄存器为管理模式 */
mrs r0,cpsr //MRS读出CPSR寄存器值到R0
bic r0,r0,#0x1f //将R0低5位清空
orr r0,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复位后是从这里开始执行,这里初始化了:
1.执行设置CPSR程序程序状态寄存器为管理模式
2.关看门狗
3.屏蔽中断
4.进入cpu_init_crit函数关闭MMU,进入lowlevel_init初始化13个BANK寄存器来初始化SDRAM
进入cpu_init_crit函数(关闭MMU):
cpu_init_crit: mov r0, #
mcr p15, , r0, c7, c7, //关闭ICaches(指令缓存,关闭是为了降低MMU查表带来的开销)和DCaches(数据缓存,DCaches使用的是虚拟地址,开启MMU之前必须关闭)
mcr p15, , r0, c8, c7, //使无效整个数据TLB和指令TLB(TLB就是负责将虚拟内存地址翻译成实际的物理内存地址) mrc p15, , r0, c1, c0,
bic r0, r0, #0x00002300 @ clear bits , : (--V- --RS) //bit8:系统不保护,bit9:ROM不保护,bit13:设置正常异常模式0x0~0x1c,即异常模式基地址为0X0
bic r0, r0, #0x00000087 @ clear bits , : (B--- -CAM) //bit0~2:禁止MMU,禁止地址对齐检查,禁止数据Cache.bit7:设为小端模式
orr r0, r0, #0x00000002 @ set bit (A) Align //bit2:开启数据Cache
orr r0, r0, #0x00001000 @ set bit (I) I-Cache //bit12:开启指令Cache
mcr p15, , r0, c1, c0,
/*
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 //退出
进入lowlevel_init函数 (初始化各个bank和SDRAM)、
lowlevel_init:
ldr r0, =SMRDATA //将SMRDATA的首地址(0x33F806C8)存到r0中
ldr r1, _TEXT_BASE //r1等于_TEXT_BASE内容,也就是TEXT_BASE(0x33F80000)
sub r0, r0, r1 //将0x33F806C8与0x33F80000相减,得到现在13个寄存器值在NOR Flash上存放的开始地址
ldr r1, =BWSCON //将BWSCON寄存器地址值存到r1中 (第一个存储器寄存器首地址)
add r2, r0, #* //每个寄存器4字节,r2=r0+13*4=NOR Flash上13个寄存器值最后一个地址
:
ldr r3, [r0], # //将r0的内容存到r3的内容中(r3等于SMRDATA里面值), 同时r0地址+=4;
str r3, [r1], # //将r3的内容存到r1所指的地址中(向寄存器地址里写入r3值),同时r1地址+=4;
cmp r2, r0 // 判断r2和r0
bne 0b //不等则跳转到第6行继续执行 mov pc, lr //跳回到返回地址中继续执行 SMRDATA:
.word (+(B1_BWSCON<<)+(B2_BWSCON<<)+(B3_BWSCON<<)+(B4_BWSCON<<)+(B5_BWSCON<<)+(B6_BWSCON<<)+(B7_BWSCON<<))
//设置每个BWSCON,注意BANK0由硬件连线决定了
.word ((B0_Tacs<<)+(B0_Tcos<<)+(B0_Tacc<<)+(B0_Tcoh<<)+(B0_Tah<<)+(B0_Tacp<<)+(B0_PMC))
.word ((B1_Tacs<<)+(B1_Tcos<<)+(B1_Tacc<<)+(B1_Tcoh<<)+(B1_Tah<<)+(B1_Tacp<<)+(B1_PMC))
.word ((B2_Tacs<<)+(B2_Tcos<<)+(B2_Tacc<<)+(B2_Tcoh<<)+(B2_Tah<<)+(B2_Tacp<<)+(B2_PMC))
.word ((B3_Tacs<<)+(B3_Tcos<<)+(B3_Tacc<<)+(B3_Tcoh<<)+(B3_Tah<<)+(B3_Tacp<<)+(B3_PMC))
.word ((B4_Tacs<<)+(B4_Tcos<<)+(B4_Tacc<<)+(B4_Tcoh<<)+(B4_Tah<<)+(B4_Tacp<<)+(B4_PMC))
.word ((B5_Tacs<<)+(B5_Tcos<<)+(B5_Tacc<<)+(B5_Tcoh<<)+(B5_Tah<<)+(B5_Tacp<<)+(B5_PMC))
//设置BANKCON0~BANKCON5
.word ((B6_MT<<)+(B6_Trcd<<)+(B6_SCAN))
.word ((B7_MT<<)+(B7_Trcd<<)+(B7_SCAN))
//设置BANKCON6~BANKCON7
.word ((REFEN<<)+(TREFMD<<)+(Trp<<)+(Trc<<)+(Tchr<<)+REFCNT)
//设置REFRESH,在S3C2440中11~17位是保留的,也即(Tchr<<16)无意义
.word 0xb1 //设置BANKSIZE,对于容量可以设置大写,多出来的空内存会被自动检测出来
.word 0x30 //设置MRSRB6
.word 0x30 //设置MRSRB7
返回到start.S继续往下看
stack_setup: //设置栈,方便调用C函数
ldr r0, _TEXT_BASE //代码段的初始地址:r0=0x33f80000
sub r0, r0, #CFG_MALLOC_LEN //留出一段内存以实现malloc:r0=0x33f50000
sub r0, r0, #CFG_GBL_DATA_SIZE //再留出一段存一些全局参数的变量:r0=0x33F4FF80 #ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) //中断与快中断的栈:r0=0x33F4DF7C
#endif
sub sp, r0, # //留出12字节内存给abort异常 设置栈顶sp=r0-12; #ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl clock_init //进入clock_init函数
#endif
这里初始化了:
5.设置栈
6.进入clock_init函数设置时钟
进入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, 0, r1, c1, c0, 0\n" /* read ctrl register */
"orr r1, r1, #0xc0000000\n" //使其从快总线模式改变为异步总线模式,在2440手册上看到
"mcr p15, 0, r1, c1, c0, 0\n" /* 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 (); //等待UCLK时钟波形稳定 /* configure MPLL */
clk_power->MPLLCON = S3C2440_MPLL_400MHZ; //FCLK=400Mhz /* some delay between MPLL and UPLL */
delay (); //等待FCLK时钟波形稳定
}
}
返回到start.S继续往下看
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代码段地址上 clear_bss:
ldr r0, _bss_start //r0=__bss_start
ldr r1, _bss_end //r0等于_bss_end里的内容,也就是_end(在u-boot.lds里定义,是存bss的结束地址)
mov r2, #0x00000000 //r2=0;用来清bss所有段 clbss_l:
str r2, [r0] //*r0=0;
add r0, r0, # //r0+=4;
cmp r0, r1
ble clbss_l //小于等于一直执行clbss_l ldr pc, _start_armboot //pc等于_start_armboot里的内容,也就是跳转到start_armboot函数
_start_armboot: .word start_armboot *(_start_armboot)=start_armboot
这里初始化了:
7.重定位(代码从Flash拷贝至SDRAM中)
8.清bss段(未初始的全局/静态变量)
9.跳转到start_armboot函数(位于u-boot-1.1.6/lib_arm/borad.c,用来实现第2阶段硬件相关的初始化)
本章小结:
uboot-第一阶段硬件初始化主要实现了:
1.执行设置CPSR程序程序状态寄存器为管理模式
2.关看门狗
3.屏蔽中断
4.关闭MMU,初始化SDRAM
5.设置栈
6.时钟设置
7.重定位(代码从Flash拷贝至SDRAM中)
8.清bss段(未初始的全局/静态变量)
9.跳转到start_armboot函数(位于u-boot-1.1.6/lib_arm/borad.c,用来实现第2阶段硬件相关的初始化)
接下来开始分析uboot-第二阶段硬件初始化。
第1阶段——uboot分析之硬件初始化start.S(4)的更多相关文章
- 第1阶段——uboot分析之硬件初始化start_armboot函数(5)
start_armboot()分析:在start.S初始化后跳转到start_armboot实现第2阶段硬件相关的初始化(烧写擦除flash,网卡驱动,usb驱动,串口驱动,从FLASH读内核,启动内 ...
- 第1阶段——uboot分析之启动函数bootm命令 (9)
本节主要学习: 详细分析UBOOT中"bootcmd=nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0"中怎么实现bootm命令 ...
- 第1阶段——u-boot分析之make指令(2)
通过make 100ask24x0_config 指令配置好芯片选型后,使用make指令来生成uboot.bin文件 本文学习目标: 对Makefile文件进行基本了解,掌握make指令是怎么实现生成 ...
- 第1阶段——uboot分析之通过nand命令读内核(8)
本节主要学习: 详细分析UBOOT中"bootcmd=nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0" 怎么实现nand命令读 ...
- 第1阶段——uboot分析之查找命令run_command函数和命令定义过程(6)
本节主要学习,run_command函数命令查找过程,命令生成过程 1.run_command函数命令查找过程分析:在u-boot界面中(main_loop();位于u-boot-1.1.6/comm ...
- 第1阶段——uboot分析之仿照bootm制作hello命令(7)
仿照bootm命令生成来制作一个hello命令,功能:打印出hello,world!和参数值 1.点击New File ,创建cmd_hello.c将./common/cmd_bootm.c的头文件复 ...
- 第1阶段——u-boot分析之make 100ask24x0_config指令(1)
本文学习目标: 掌握"make 100ask24x0_config"指令在Makefile和mkconfig文件中是怎么实现配置芯片选型 1.执行make 100a ...
- 第2阶段——编写uboot之硬件初始化和制作链接脚本lds(1)
目标: 1.关看门狗 2.设置时钟 3.初始化SDRAM (初始化寄存器以及清除bss段) 4.重定位 (将nand/nor中代码COPY到链接地址上,需要初始化nandflash,读flash) 5 ...
- u-boot分析(九)----nand flash初始化|nand flash读写分析
u-boot分析(九) 上篇博文我们按照210的启动流程,分析到了初始化串口,由于接下来的取消存储保护不是很重要,所以我们今天按照u-boot的启动流程对nand flash初始化进行分析. 今天我们 ...
随机推荐
- hexo博客MathJax公式渲染问题
这个问题自己很早以前便碰到了,用MathJax语法写的一些公式,在本地Markdown编译器上渲染是没问题的,可是部署到hexo博客中就出现问题了,之前我是使用图片代替公式应付过去了,今天从网上找了一 ...
- JavaScript中的call()、apply()与bind():
关于call()与apply(): 在JavaScript中,每个函数都有call与apply(),这两个函数都是用来改变函数体内this的指向,并调用相关的参数. 看一个例子: 定义一个animal ...
- This Adroid SDK r…
有时候开启eclipse时会出现如下提示框: This Adroid SDK requires Android Developer
- 在webpack中使用Code Splitting--代码分割来实现vue中的懒加载
当Vue应用程序越来越大,使用Webpack的代码分割来懒加载组件,路由或者Vuex模块, 只有在需要时候才加载代码. 我们可以在Vue应用程序中在三个不同层级应用懒加载和代码分割: 组件,也称为异步 ...
- MQ选型对比文档
几种MQ产品说明: ZeroMQ : 扩展性好,开发比较灵活,采用C语言实现,实际上他只是一个socket库的重新封装,如果我们做为消息队列使用,需要开发大量的代码 RabbitMQ ...
- centos7安装mysql(yum)
centos7安装mysql(yum) ----安装环境----依赖安装----检查mysql是否已安装----安装----验证是否添加成功----选择要启用的mysql版本----通过Yum安装my ...
- react入门之使用webpack搭配环境(一)
react入门之搭配环境(一) 如果你想直接上手开发,而跳过这些搭配环境的繁琐过程,推荐你使用官方的create-react-app命令 npm install -g create-react-app ...
- Hadoop1.0.3安装部署
0x00 大数据平台相关链接 官网:http://hadoop.apache.org/ 主要参考教程:http://www.cnblogs.com/xia520pi/archive/2012/05/1 ...
- 【Java集合学习】HashMap源码之“拉链法”散列冲突的解决
1.HashMap的概念 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io ...
- Android 开源可缩放平移的绘画板
ScaleSketchPadDemo 此项目包含两个模块 app1 为普通绘画板 app2 为可所发的绘画板 方便各位Android 开发者理解和使用 用法: 进入项目根目录:https://gith ...