注:以下内容学习于韦东山老师arm裸机第一期****
一.arm架构的异常与中断的处理
1.1 ARM对异常的处理流程
1.1.1 软件初始化
a. 设置中断源,让他可以产生中断
b. 设置中断优先级
c. 设置中断总开关
1.1.2 正常执行程序
对于不同的异常跳去不同的地址执行程序(这些地址一般是排在一起的,叫做异常向量),
这些地址上只是一条跳转指令,跳去执行其它函数,如下表
1.1.6 这些函数做什么事情?
a.保存现场(各种寄存器)
b.调用不同的处理函数
分辨中断源
调用不同处理函数
c.恢复现场
1.2 ARM异常种类
在uboot 1.1.6中有如下代码
-
_start: b reset /* 0地址,复位异常 */ -
ldr pc, _undefined_instruction /* 4地址,未定义指令 */ -
ldr pc, _software_interrupt /* 8地址,软中断指令 */ -
ldr pc, _prefetch_abort /* 12地址,指令预取异常,如果处理器预取的指令不存在,或者该地址不允许当前指令访问 */ -
ldr pc, _data_abort /* 14地址,数据访问终止,如果数据访问指令的目标地址不存在,或者该地址不允许当前指令访问 */ -
ldr pc, _not_used -
ldr pc, _irq /* 24地址,中断异常 */ -
ldr pc, _fiq /* 28地址,快中断异常 */ -
_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
中断发生时CPU会强制调到0x18(24)的地方,然后跳转到其它函数进行处理
二.CPU模式与状态
2.1 CPU的State
2.1.1 ARM State
在这种状态下,使用arm指令集,每个指令占据4字节(被编译成的机器码占据4个字节)
2.1.2 Thumb State
在这种状态下,使用thumb指令集,每个指令占据2字节(被编译成的机器码占据2个字节)
thumb指令集是arm指令集压缩形式的子集,thumb更加高效,可以减少存储空间,对于嵌入式系统nor,sdram很大,不需要节省这点空间
Thumb 不是一个完整的体系结构,不能指望处理只执行Thumb 指令而不支持 ARM 指令集.因此,Thumb 指令只需要支持通用功能,必要时可以借助于完善的 ARM 指令集
2.1.3 将一个程序使用Thumb编译示例
对于C程序,在编译时添加-mthumb即可
修改Makefile如下:
-
objs = uart.o main.o start.o SetTacc.o my_printf.o lib1funcs.o init.o -
A = test -
all:$(objs) -
#arm-linux-ld -Ttext 0 -Tdata 0x800 $^ -o $(A).elf -
arm-linux-ld -T sdram.lds $^ -o $(A).elf -
arm-linux-objcopy -O binary -S $(A).elf $(A).bin -
arm-linux-objdump -D $(A).elf > $(A).dis -
%.o:%.c -
arm-linux-gcc -mthumb -c -o [email protected] $< -
%.o:%.S -
arm-linux-gcc -c -o [email protected] $< -
clean: -
rm *.o *.elf *.bin
对于.S程序,需要在代码中指定,在前面添加.code 32表示后续的指令使用arm指令集,在sdram_init前面添加.code 16表示下面的都使用thumb指令集,在前面需要使用bx指令进行状态切换,bx跳转的地址bit0等于1时就会进行状态的切换
代码如下:
-
.text -
.code 32 /* 表示下面的代码使用arm指令集 */ -
.global _start -
_start: -
/* 省略一些初始化代码 */ -
/* 获得thumb_func地址 */ -
adr r0, thumb_func -
/* 由于地址是4字节对齐,最后一位等于0,因此设置最后一位等于1,切换到thumb指令集 */ -
add r0, r0,#1 -
bx r0 -
.code 16 /* 表示下面的代码使用thumb指令集 */ -
thumb_func: -
bl SdramInit -
mov r0, #0 -
ldr r1, =_start -
ldr r2, =bss_start -
sub r2, r2, r1 -
bl Copy2Sdram -
ldr r0, =bss_start -
ldr r1, =bss_end -
bl CleanBss -
//bl main -
//ldr pc, =main -
/* 对thumb指令无法直接执行ldr pc, =main */ -
ldr r0, =main -
mov pc, r0 -
halt: -
b halt
编译出来可以发现bin文件变小了很多
2.2 CPU的工作模式
2.2.1 用户模式->正常程序执行的模式
2.2.2 系统模式
2.2.3 异常模式
a.未定义指令模式
b.管理模式
c.中止模式
c.1 指令预取终止(CPU是流水线操作的,在执行当前指令的时候读取第二条指令,执行第三条指令,第三条指令的读取叫做预取,可能会出错)
c.2 数据访问终止
d.中断模式
e.快中断模式
2.2.4 快速中断模式
其中,除了用户模式的其它六种模式又称为特权模式(Privileged Modes),在这六种模式下可以操作CPSR寄存器任意切换其它模式,
但是在用户模式下不可以直接进入其它模式
2.3 每种工作模式的差别
3.3.1 主要是寄存器之间的差别,见下图
3.3.2 未备份寄存器: r0-r7
备份寄存器: r8-14
程序计数器: r15
每个模式下都有自己的r13,r14寄存器sp与lr
对于快中断模式,有自己的备份寄存器r8-r14,在中断发生时不需要保存r8-r14这些寄存器,因此可以加快处理速度
在linux系统中并不会使用快中断模式
3.3.3 程序状态寄存器CPSR,SPSR,格式如下图
CPSR寄存器
a.M0-M4表示CPU当前处于哪种工作模式,可以读取进行修改(如果处于用户模式则无法修改),见下图
b.bit5-T->表示当前使用的指令集是ARM还是thmub
c.bit6-F->表示快中断使能
d.bit7-I->表示中断使能
e.bit8-bit27->保留位
f.bit28-bit31->状态位
例如 cmp R0,R1 (如果相等会将bit30位置1)
beq xxx (会去判断bit30是不是为1,是1的话则会跳转)
SPSR寄存器
发生异常时,这个寄存器会来保存被中断模式下的CPSR寄存器
2.4 异常的处理流程
2.4.1 进入异常时硬件完成:
1.将下一条指令的地址保存在lr寄存器中,即lr_异常 = 被中断模式的下一条指令的地址(pc + 4 / pc + 8)
2.将CPSR寄存器保存在异常模式下的SPSR,即SPSR_异常 = CPSR
3.修改CPSR的模式位(M0-M4),进入异常模式
4.调到向量表
2.4.2 退出异常时硬件完成:
1.PC = lr_异常 - offset,具体见下表
例如发生swi异常时,可以PC = lr_svc返回
发生fiq异常时,可以执行PC = lr_fiq - 4返回
2. 恢复CPSR的值,CPSR = SPSR_异常
3. 清中断(如果发生中断)
三.und异常模式程序示例
-
.text -
.global _start -
_start: -
b reset /* vector 0 : reset */ -
b do_und /* vector 4 : und */ -
do_und: -
/* 执行到这里之前: -
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址 -
* 2. SPSR_und保存有被中断模式的CPSR -
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式 -
* 4. 跳到0x4的地方执行程序 -
*/ -
/* sp_und未设置, 先设置它 */ -
ldr sp, =0x34000000 -
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */ -
/* lr是异常处理完后的返回地址, 也要保存 */ -
stmdb sp!, {r0-r12, lr} -
/* 保存现场 */ -
/* 处理und异常 */ -
mrs r0, cpsr -
ldr r1, =und_string -
bl printException -
/* 恢复现场 */ -
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ -
und_string: -
.string "undefined instruction exception" -
reset: -
/* 关闭看门狗 */ -
ldr r0, =0x53000000 -
ldr r1, =0 -
str r1, [r0] -
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ -
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ -
ldr r0, =0x4C000000 -
ldr r1, =0xFFFFFFFF -
str r1, [r0] -
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ -
ldr r0, =0x4C000014 -
ldr r1, =0x5 -
str r1, [r0] -
/* 设置CPU工作于异步模式 */ -
mrc p15,0,r0,c1,c0,0 -
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA -
mcr p15,0,r0,c1,c0,0 -
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) -
* m = MDIV+8 = 92+8=100 -
* p = PDIV+2 = 1+2 = 3 -
* s = SDIV = 1 -
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M -
*/ -
ldr r0, =0x4C000004 -
ldr r1, =(92<<12)|(1<<4)|(1<<0) -
str r1, [r0] -
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 -
* 然后CPU工作于新的频率FCLK -
*/ -
/* 设置内存: sp 栈 */ -
/* 分辨是nor/nand启动 -
* 写0到0地址, 再读出来 -
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 -
* 否则就是nor启动 -
*/ -
mov r1, #0 -
ldr r0, [r1] /* 读出原来的值备份 */ -
str r1, [r1] /* 0->[0] */ -
ldr r2, [r1] /* r2=[0] */ -
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ -
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ -
moveq sp, #4096 /* nand启动 */ -
streq r0, [r1] /* 恢复原来的值 */ -
bl sdram_init -
//bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ -
/* 重定位text, rodata, data段整个程序 */ -
bl copy2sdram -
/* 清除BSS段 */ -
bl clean_bss -
bl uart0_init -
/* 故意加入一条未定义指令 */ -
und_code: -
/* 注意,必须是未定义指令,已定义指令见下表 */ -
.word 0xdeadc0de /* 未定义指令 */ -
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ -
ldr pc, =main /* 绝对跳转, 跳到SDRAM */ -
halt: -
b halt
三.und异常模式程序的改进
3.1 问题引入及改进
a. 在上面的代码中,未定义指令放在了代码重定位之后,如果执行到异常处理函数时,一旦发生未定义异常,程序将会跳转(相对跳转)
反汇编代码如下,由于使用的的b相对跳转,程序并不会调到30000008的地址,而是会跳转到0x8的地址,一旦前面的程序过多,跳转地址的偏移较高,超过4096时,
若是NAND启动便会出错,因为RAM只有4K
-
30000000 <_start>: -
30000000: ea00000e b 30000040 <reset> -
30000004: eaffffff b 30000008 <do_und> -
30000008 <do_und>:
改进方法:使用绝对跳转,跳转到sdram中去,相关代码如下:
-
.text -
.global _start -
_start: -
b reset /* vector 0 : reset */ -
/* 注意,不可以使用ldr pc, =und_addr,这样会将und_addr的值存放在某个内存再去读出来,有可能存放的地方超过4K -
* 而且,一般放在汇编文件的最后边去,然后去读取,如果汇编文件超过4K也会出错 -
ldr pc, und_addr /* vector 4 : und */ /* 从und_addr地址处读值赋给PC,会读到do_und的地址 */ -
und_addr: -
.word do_und -
do_und:
对应反汇编如下:
-
30000000 <_start>: -
30000000: ea000012 b 30000050 <reset> -
30000004: e51ff004 ldr pc, [pc, #-4] ; 30000008 <und_addr> -
30000008 <und_addr>: -
30000008: 3000000c andcc r0, r0, ip -
3000000c <do_und>:
b.在重定位完之后,后面的代码都可能超过4K,因此要跳到sdram中去执行,代码如下
-
ldr pc, =sdram -
sdram: -
bl uart0_init -
bl print1 -
/* 故意加入一条未定义指令 */ -
und_code: -
.word 0xdeadc0de /* 未定义指令 */ -
bl print2 -
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ -
ldr pc, =main /* 绝对跳转, 跳到SDRAM */ -
halt: -
b halt
c.在定义字符串时有可能字符串的长度不是4字节对齐,因此在后面要调价.align = 4,如下:
-
und_string: -
.string "undefined instruction exception"
-
.text -
.global _start -
_start: -
b reset /* vector 0 : reset */ -
ldr pc, und_addr /* vector 4 : und */ -
und_addr: -
.word do_und -
do_und: -
/* 执行到这里之前: -
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址 -
* 2. SPSR_und保存有被中断模式的CPSR -
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式 -
* 4. 跳到0x4的地方执行程序 -
*/ -
/* sp_und未设置, 先设置它 */ -
ldr sp, =0x34000000 -
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */ -
/* lr是异常处理完后的返回地址, 也要保存 */ -
stmdb sp!, {r0-r12, lr} -
/* 保存现场 */ -
/* 处理und异常 */ -
mrs r0, cpsr -
ldr r1, =und_string -
bl printException -
/* 恢复现场 */ -
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ -
und_string: -
.string "undefined instruction exception" -
.align 4 -
reset: -
/* 关闭看门狗 */ -
ldr r0, =0x53000000 -
ldr r1, =0 -
str r1, [r0] -
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ -
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ -
ldr r0, =0x4C000000 -
ldr r1, =0xFFFFFFFF -
str r1, [r0] -
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ -
ldr r0, =0x4C000014 -
ldr r1, =0x5 -
str r1, [r0] -
/* 设置CPU工作于异步模式 */ -
mrc p15,0,r0,c1,c0,0 -
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA -
mcr p15,0,r0,c1,c0,0 -
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) -
* m = MDIV+8 = 92+8=100 -
* p = PDIV+2 = 1+2 = 3 -
* s = SDIV = 1 -
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M -
*/ -
ldr r0, =0x4C000004 -
ldr r1, =(92<<12)|(1<<4)|(1<<0) -
str r1, [r0] -
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 -
* 然后CPU工作于新的频率FCLK -
*/ -
/* 设置内存: sp 栈 */ -
/* 分辨是nor/nand启动 -
* 写0到0地址, 再读出来 -
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 -
* 否则就是nor启动 -
*/ -
mov r1, #0 -
ldr r0, [r1] /* 读出原来的值备份 */ -
str r1, [r1] /* 0->[0] */ -
ldr r2, [r1] /* r2=[0] */ -
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ -
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ -
moveq sp, #4096 /* nand启动 */ -
streq r0, [r1] /* 恢复原来的值 */ -
bl sdram_init -
//bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ -
/* 重定位text, rodata, data段整个程序 */ -
bl copy2sdram -
/* 清除BSS段 */ -
bl clean_bss -
ldr pc, =sdram -
sdram: -
bl uart0_init -
bl print1 -
/* 故意加入一条未定义指令 */ -
und_code: -
.word 0xdeadc0de /* 未定义指令 */ -
bl print2 -
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ -
ldr pc, =main /* 绝对跳转, 跳到SDRAM */ -
halt: -
b halt
四.swi异常模式程序示例
4.1 swi异常介绍
在linux系统中,app运行于用户模式,这种模式受限制,不可访问硬件,当应用程序想要访问硬件时,必须切换模式,怎么切换?
swi就是软件中断,由于用户模式不能够修改cpsr寄存器来切换模式.因此,执行swi指令 + val来发生异常,可以根据val值判断为什么发生异常
4.2 程序示例
-
.text -
.global _start -
_start: -
b reset /* vector 0 : reset */ -
ldr pc, und_addr /* vector 4 : und */ -
ldr pc, swi_addr /* vector 8 : swi */ -
und_addr: -
.word do_und -
swi_addr: -
.word do_swi -
do_und: -
/* 执行到这里之前: -
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址 -
* 2. SPSR_und保存有被中断模式的CPSR -
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式 -
* 4. 跳到0x4的地方执行程序 -
*/ -
/* sp_und未设置, 先设置它 */ -
ldr sp, =0x34000000 -
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */ -
/* lr是异常处理完后的返回地址, 也要保存 */ -
stmdb sp!, {r0-r12, lr} -
/* 保存现场 */ -
/* 处理und异常 */ -
mrs r0, cpsr -
ldr r1, =und_string -
bl printException -
/* 恢复现场 */ -
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ -
und_string: -
.string "undefined instruction exception" -
.align 4 -
do_swi: -
/* 执行到这里之前: -
* 1. lr_svc保存有被中断模式中的下一条即将执行的指令的地址 -
* 2. SPSR_svc保存有被中断模式的CPSR -
* 3. CPSR中的M4-M0被设置为10011, 进入到svc模式 -
* 4. 跳到0x08的地方执行程序 -
*/ -
/* sp_svc未设置, 先设置它 */ -
ldr sp, =0x33e00000 -
/* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */ -
/* lr是异常处理完后的返回地址, 也要保存 */ -
stmdb sp!, {r0-r12, lr} -
/* 保存现场 */ -
/* 处理swi异常 */ -
mrs r0, cpsr -
ldr r1, =swi_string -
bl printException -
/* 恢复现场 */ -
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ -
swi_string: -
.string "swi exception" -
.align 4 -
reset: -
/* 关闭看门狗 */ -
ldr r0, =0x53000000 -
ldr r1, =0 -
str r1, [r0] -
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ -
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ -
ldr r0, =0x4C000000 -
ldr r1, =0xFFFFFFFF -
str r1, [r0] -
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ -
ldr r0, =0x4C000014 -
ldr r1, =0x5 -
str r1, [r0] -
/* 设置CPU工作于异步模式 */ -
mrc p15,0,r0,c1,c0,0 -
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA -
mcr p15,0,r0,c1,c0,0 -
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) -
* m = MDIV+8 = 92+8=100 -
* p = PDIV+2 = 1+2 = 3 -
* s = SDIV = 1 -
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M -
*/ -
ldr r0, =0x4C000004 -
ldr r1, =(92<<12)|(1<<4)|(1<<0) -
str r1, [r0] -
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 -
* 然后CPU工作于新的频率FCLK -
*/ -
/* 设置内存: sp 栈 */ -
/* 分辨是nor/nand启动 -
* 写0到0地址, 再读出来 -
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 -
* 否则就是nor启动 -
*/ -
mov r1, #0 -
ldr r0, [r1] /* 读出原来的值备份 */ -
str r1, [r1] /* 0->[0] */ -
ldr r2, [r1] /* r2=[0] */ -
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ -
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ -
moveq sp, #4096 /* nand启动 */ -
streq r0, [r1] /* 恢复原来的值 */ -
bl sdram_init -
//bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ -
/* 重定位text, rodata, data段整个程序 */ -
bl copy2sdram -
/* 清除BSS段 */ -
bl clean_bss -
/* 复位之后, cpu处于svc模式 -
* 现在, 切换到usr模式 -
*/ -
mrs r0, cpsr /* 读出cpsr */ -
bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */ -
msr cpsr, r0 -
/* 设置 sp_usr */ -
ldr sp, =0x33f00000 -
ldr pc, =sdram -
sdram: -
bl uart0_init -
bl print1 -
/* 故意加入一条未定义指令 */ -
und_code: -
.word 0xdeadc0de /* 未定义指令 */ -
bl print2 -
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */ -
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ -
ldr pc, =main /* 绝对跳转, 跳到SDRAM */ -
halt: -
b halt
注意,CPU一上电处于管理模式,切换到用户模式再来执行SWI指令(在别的模式也可以执行,这么做只是为了测试用户模式),修改CPSR寄存器的M4-M0为0b10000即可进入用户模式
读出swi + val的val值,由于在发生异常时,lr寄存器会保存异常的下一条指令的地址,因此发生swi异常指令的地址就是lr-4,根据地址及机器码格式便可以找出val值
代码如下:
-
.text -
.global _start -
_start: -
b reset /* vector 0 : reset */ -
ldr pc, und_addr /* vector 4 : und */ -
ldr pc, swi_addr /* vector 8 : swi */ -
und_addr: -
.word do_und -
swi_addr: -
.word do_swi -
do_und: -
/* 执行到这里之前: -
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址 -
* 2. SPSR_und保存有被中断模式的CPSR -
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式 -
* 4. 跳到0x4的地方执行程序 -
*/ -
/* sp_und未设置, 先设置它 */ -
ldr sp, =0x34000000 -
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */ -
/* lr是异常处理完后的返回地址, 也要保存 */ -
stmdb sp!, {r0-r12, lr} -
/* 保存现场 */ -
/* 处理und异常 */ -
mrs r0, cpsr -
ldr r1, =und_string -
bl printException -
/* 恢复现场 */ -
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ -
und_string: -
.string "undefined instruction exception" -
.align 4 -
do_swi: -
/* 执行到这里之前: -
* 1. lr_svc保存有被中断模式中的下一条即将执行的指令的地址 -
* 2. SPSR_svc保存有被中断模式的CPSR -
* 3. CPSR中的M4-M0被设置为10011, 进入到svc模式 -
* 4. 跳到0x08的地方执行程序 -
*/ -
/* sp_svc未设置, 先设置它 */ -
ldr sp, =0x33e00000 -
/* 保存现场 */ -
/* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */ -
/* lr是异常处理完后的返回地址, 也要保存 */ -
stmdb sp!, {r0-r12, lr} -
/* 由于先调用了printException函数,会使用r0-r3来传递参数,并且使用完不会恢复,因此不能用r0-r3保存lr寄存器的值,r4-r8也会被使用,但是使用完后寄存器的值被恢复原来的值 */ -
mov r4, lr -
/* 处理swi异常 */ -
mrs r0, cpsr -
ldr r1, =swi_string -
bl printException -
sub r0, r4, #4 -
bl printSWIVal -
/* 恢复现场 */ -
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ -
swi_string: -
.string "swi exception" -
.align 4 -
reset: -
/* 关闭看门狗 */ -
ldr r0, =0x53000000 -
ldr r1, =0 -
str r1, [r0] -
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ -
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ -
ldr r0, =0x4C000000 -
ldr r1, =0xFFFFFFFF -
str r1, [r0] -
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ -
ldr r0, =0x4C000014 -
ldr r1, =0x5 -
str r1, [r0] -
/* 设置CPU工作于异步模式 */ -
mrc p15,0,r0,c1,c0,0 -
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA -
mcr p15,0,r0,c1,c0,0 -
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) -
* m = MDIV+8 = 92+8=100 -
* p = PDIV+2 = 1+2 = 3 -
* s = SDIV = 1 -
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M -
*/ -
ldr r0, =0x4C000004 -
ldr r1, =(92<<12)|(1<<4)|(1<<0) -
str r1, [r0] -
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 -
* 然后CPU工作于新的频率FCLK -
*/ -
/* 设置内存: sp 栈 */ -
/* 分辨是nor/nand启动 -
* 写0到0地址, 再读出来 -
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 -
* 否则就是nor启动 -
*/ -
mov r1, #0 -
ldr r0, [r1] /* 读出原来的值备份 */ -
str r1, [r1] /* 0->[0] */ -
ldr r2, [r1] /* r2=[0] */ -
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ -
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ -
moveq sp, #4096 /* nand启动 */ -
streq r0, [r1] /* 恢复原来的值 */ -
bl sdram_init -
//bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ -
/* 重定位text, rodata, data段整个程序 */ -
bl copy2sdram -
/* 清除BSS段 */ -
bl clean_bss -
/* 复位之后, cpu处于svc模式 -
* 现在, 切换到usr模式 -
*/ -
mrs r0, cpsr /* 读出cpsr */ -
bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */ -
msr cpsr, r0 -
/* 设置 sp_usr */ -
ldr sp, =0x33f00000 -
ldr pc, =sdram -
sdram: -
bl uart0_init -
bl print1 -
/* 故意加入一条未定义指令 */ -
und_code: -
.word 0xdeadc0de /* 未定义指令 */ -
bl print2 -
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */ -
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ -
ldr pc, =main /* 绝对跳转, 跳到SDRAM */ -
halt: -
b halt -
printSWIVal函数如下,其中swi指令格式见下图,取出低24位即可: -
void printSWIVal(unsigned int *pSWI) -
{ -
puts("SWI val = "); -
printHex(*pSWI & ~0xff000000); -
puts("\n\r");