专题5-核心初始化
第1课-异常向量表
一.概念解析
- 异常定义
因为内部或者外部的一些事,导致处理器停下正在处理的工作,转而出处理这些发生的事件。
- 异常类型
Arm处理器有七种异常类型,依次是:重启键,用户自己的命令未识别,软中断,预取指令识别,取数据失败,中断,快速中断。
- 异常向量
当一种异常发生的时候,ARM处理器会跳转到对应异常的固定地址去执行异常处理程序,而这个固定的地址,就称为异常向量。
- 向量表
由七个异常向量以及其处理函数跳转关系组成的表就是异常向量表:
0x00000000: b reset
0x00000004: ldr pc, _undefined_instruction
0x00000008: ldr pc, -software_interrupt
0x0000000c: ldr pc, _prefetch_abort
0x00000010: ldr pc, _data_abort
0x00000014: ldr pc, _not_used
0x00000018: ldr pc, _irq
0x0000001c: ldr pc, _lrq
二.代码编写
start.s
gboot.lds
makefile
start.S
.text
.global _start
_start:
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction @使得伪指令变成了装载指令
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
undefined_instruction:
nop
software_interrupt:
nop
prefetch_abort:
nop
data_abort:
nop
not_used:
nop
irq:
nop
fiq:
nop
reset:
nop
gboot.lds
OUTPUT_ARCH(arm) //宏定义,输出格式是arm的
ENTRY(_start) //宏定义,输出程序的入口
SECTIONS {
. = 0x30008000; //起始地址
. = ALIGN(4); //四字节,对齐的处理
.text : //代码段
{
start.o (.text)
*(.text)
}
. = ALIGN(4);
.data : //数据段
{
*(.data)
}
. = ALIGN(4);
bss_start = .;
.bss : //bss段
{
*(.bss)
}
bss_end = .;
}
makefile
all: start.o
arm-linux-ld -Tgboot.lds -o gboot.elf $^
arm-linux-objcopy -O binary gboot.elf gboot.bin
%.o : %.S
arm-linux-gcc -g -c $^ //通用规则
%.o : %.c
arm-linux-gcc -g -c $^
.PHONY: clean
clean:
rm *.o *.elf *.bin
三. 210处理器BL1头信息添加
我们编写的gboot充当BL1的作用,在210的irom中自带BL0,上电后率先运行的就是BL0,在BL0运行之后会进行一个校验。BL1之前是有16个字节的头的,LB0将BL1拷贝到irom里面后,会通过对BL1的计算,得到校验码。将这个经验吗和在BL1的头中填入的校验码进行比对,若是比对结果一样,BL1可以运行;若是不一样就BL1就不能运行,若BL1之中没有头文件,肯定BL1就不会运行。头文件格式如下:
0x0写的是:BL1的大小;
0x4没有用到;
0x8写的是用户写入的校验码大小;
0xc没有用到。
第2课-设置svc模式
这一部分不需要做2440、6410和210的区分
这是linux系统的其中工作模式,对于bootloader它的工作模式是supervisor,一种比较高级的工作模式,本节课的重点就是将处理它设置在supervisor模式。具体依靠的是程序状态字寄存器:
0-4字段设置的是工作模式,,将这5个字设置为10011就可以。
步骤:
(1)将这五个字段全部置零:bic
(2)将其中的三个字段置1:orr
(3)为了访问CPSR或者SPSR,需要使用mrs和msr
对start.s进行改动,如下:
.text
.global _start
_start:
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
undefined_instruction:
nop
software_interrupt:
nop
prefetch_abort:
nop
data_abort:
nop
not_used:
nop
irq:
nop
fiq:
nop
reset:
bl set_svc
set_svc:
mrs r0, cpsr @从cpsr取值
bic r0, r0, #0x1f
orr r0, r0, #0xd3 @理论上是#0x13用来表示10011,这里用#0xd3来表示@11010011,设置I和F两位为1,屏蔽了中断与快速中断
msr cpsr, r0 @将值放在cpsr
mov pc, lr
第3课-关闭看门狗
- 作用
在嵌入式领域,有些系统需要长期运行在无人看守的环境。在运行的过程中,难免不出现系统死机的情况,这时候需要系统自带的一种自动重启的功能。Watchdaog一般是一个硬件模块,起作用就是在系统死机时,帮助系统实现自动重启。
- 工作方式
Watchdaog在在硬件上实现了计时功能,启动计时后,用户(软件)必须在计时结束前重新开始计时,俗称“喂狗”,如果到超时的时候还没有重新开始计时,那么它就认为系统是死机了,就自动重启系统。
- 原理图
时钟源经过WTCON[15:8]和WTCON[4:3]分频器的处理,得到我们需要的时钟信号,之后通过WTCNT进行计数,当它检测到0的时候,没有新的WTDAT信号来重置,下面通过WTCON[0]进行重启。
看门狗有这么好的作用,之所以要关闭看门狗,因为bootloader是一个简单的程序,一般是不需要使用的,使用它会增加“喂狗”的操作,增加系统而定负担。
我们通过寄存器来完成这个操作。
在2440中,寄存器名字为WTCON,如下:
在6410中,寄存器名字为WTCON,如下:
设置与2440一样。
在210中的WTCON的地址是0xE2700000,其他设置与2440一样。
- 代码实现
修改start.s的下半部分,如下:
reset:
bl set_svc
bl disable_watchdog
set_svc:
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
mov pc, lr
#define pWTCON 0x53000000
disable_watchdog:
ldr r0, =pWTCON @把地址装载到r0
mov r1, #0x0 @mov只能访问通用寄存器,都设置为0
str r1, [r0] @将0的值都放在r0中
mov pc, lr
第4课-关闭中断
步骤:
(1) 在CPSR中将I位(中断位)和F位(快速中断位)设置为1,将它们关闭,这在我们设置svc模式的时候已经设置了#0xd3
(2) 设置中断屏蔽寄存器,把中断屏蔽掉
2440:
在s3c2440的芯片手册中,找到下图:
当中断请求前来的时候,会将请求指令记录在SRCPND里面,中断的请求要送到处理器,还要送到MASK寄存器,若将MASK(屏蔽寄存器)屏蔽掉请求信号,就不会有中断操作。在芯片手册的233页找到如下的内容:
这是32位的寄存器,将其中某一位写做1,对应的中断就会被屏蔽,也就是,将所有的位都置1就好了。
代码:
2440,将start.s作如下修改:
reset:
bl set_svc
bl disable_watchdog
bl disable_interrupt
set_svc:
mrs r0, cpsr
bic r0, r0,#0x1f
orr r0, r0,#0xd3
msr cpsr, r0
mov pc, lr
#define pWTCON 0x53000000
disable_watchdog:
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
mov pc, lr
disable_interrupt:
mvn r1, #0x0 @取反后赋值
ldr r0, =0x4a000008 @将寄存器地址记录下
str r1, [r0] @将值幅值给地址
mov pc, lr @返回原位置
6410:
6410中有两个中断使能寄存器,如下:
6410中有两个中断关闭寄存器,如下:
代码如下:
reset:
bl set_svc
bl disable_watchdog
bl disable_interrupt
set_svc:
mrs r0, cpsr
bic r0, r0,#0x1f
orr r0, r0,#0xd3
msr cpsr, r0
mov pc, lr
#define pWTCON 0x7e004000
disable_watchdog:
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
mov pc, lr
disable_interrupt:
mvn r1,#0x0
ldr r0,=0x71200014
str r1,[r0]
ldr r0,=0x71300014
str r1,[r0]
mov pc, lr
210:
210与6410类似都是采用向量中断,也分为使能IntEnable寄存器和屏蔽IntEnable寄存器。由于210分为更多功能的中断,它的屏蔽寄存器有4个,保存的地址分别是:
在6410基础上改写的代码是:
disable_interrupt:
mvn r1,#0x0
ldr r0,=0xf2000014
str r1,[r0]
ldr r0,=0xf2100014
str r1,[r0]
ldr r0,=0xf2200014
str r1,[r0]
ldr r0,=0xf2300014
str r1,[r0]
mov pc, lr
第5课-关闭mmu与cache
- ARM存储体系
金字塔顶端是各种寄存器,访问速度快但是数量有限;第二层是TCM,包括内存与cache,数量较多,但速度相对来说慢一些;辅助存储器数量更多,但是访问速度更加缓慢。
- cache
cache的存储图如下:
对比有无cache的访问主内存的方式,有cache的系统对主内存的访问效率更高。
cache是一种容量小但存取速度非常快的存储器,它保存最近用到的存储器中数据的拷贝。对于程序员来说,cache是透明的。它自动决定保存哪些数据、覆盖哪些数据。按照功能划分:
I-Cache: 指令Cache,用于存放指令
D-Cache:数据Cache,用于存放数据
对于2440的存储结构如下:
其中既有Data Cache有Instruction Cache,它们的大小都是16Kb。210中的连个cache大小是32KB。
- 虚拟地址
虚拟地址:程序中使用的地址。
物理地址:物理存储单元实际的地址
使用虚拟地址
-可以让进程使用更大的空间
-可以解决冲突
- mmu作用
在Arm11之前,根据虚拟地址就可访问cache,但是在Arm11本身包括之后的操作,需要实际的物理地址才能访问cache
- 关闭cache和mmu
mmu与cache的使用是需要一系列的配置才能使用的,并不是不用。只是在初始化的时候,必须将它们关闭,控制它们的是协处理器CP15。
2440:
在920核心手册里面,可以看到CP15有一个control register寄存器,它也是32位的寄存器,12位作用是Icache enable,第2为控制Dcache enable,第0位控制MMU,如下:
我们还要设置CP15里面的第7个寄存器,使得原本的数据无效,指令是:
MCR p15, 0, Rd, c7, c7, 0
步骤:
(1) 使Icache和Dcache失效
(2) 关闭Icache、Dcache和关闭mmu
修改start.s代码为:
reset:
bl set_svc
bl disable_watchdog
bl disable_interrupt
bl disable_mmu
disable_mmu:
mcr p15,0,r0,c7,c7,0 @使Icache和Dcache失效
mrc p15,0,r0,c1,c0,0 @将寄存器读出来,固定的格式,与下面的配对
bic r0, r0, #0x00000007 @使得第0和2位置零,12位可以不去处理
mcr p15,0,r0,c1,c0,0 @将寄存器写进去,固定的格式,与上面的配对
mov pc, lr @返回主函数的位置,bl将返回地址存在lr寄存器,当程序@运行结束,将返回地址传给pc指针
在6410与210的芯片上,CP15的结构是一样的,不用修改。