1、异常向量表
/******下面这些就是异常向量表*****/
.globl _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
手册异常向量表定义
2、程序
.text
.global _start
_start:
b reset /*vector 0 一上电复位,是从0地址开始执行,跳到reset*/
b do_und /*vector 4 如果发生未定义指令异常,硬件就会在自动跳转0x04地址未定义指令异常处,执行do_und*/
/*假设一上电从0地址开始执行,reset,做一系列初始化之后
*故意加入一条未定义指令
*/
do_und:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x4的地方执行程序
*/
/*需要从新设置sp栈,指向某一块没有使用的地址*/
/* sp_und未设置, 先设置它 */
ldr sp,=0x34000000 /*指向SDRAM的一块内存上*/
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* 发生异常时,当前被中断的地址会保存在lr寄存器中 先减后存*/
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!,{r0-r12,lr} /*先减后存*/
/* 1、保存现场 */
/* 2、处理und异常 */
mrs r0,cpsr
ldr r1,=und_string
bl printException
/* 3、恢复现场 */
ldmia sp!,{r0-r12,pc}^ /* ^ 会把und_spsr的值恢复到cpsr里 */
und_string:
.string "undefined instruction exception"
如何定义字符串,可以百度搜索 arm-linux-gcc 汇编 定义字符串
官方的说明文档
http://web.mit.edu/gnu/doc/html/as_7.html
.string "str" /*汇编代码定义字符串*/
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] /* 恢复原来的值 */
/*初始化SDRAM*/
bl sdram_init
/*初始化UART0*/
bl uart0_init
/*重定位text rodata data段整个程序*/
bl copy2sdram
/*清除BSS段*/
bl clean_bss
bl print1
und_code:
.word 0xff123456 /*未定义指令*/
bl print2
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main /* **跳转, 跳到SDRAM */
halt:
b halt
如何处理这个异常呢?
直接print打印一句话,新建一个exception.c文件
#include "uart.h"
void printException(unsigned int cpsr, char *str) //cpsr打印相应的寄存器,str打印一个字符串
{
puts("Exception! cpsr = ");\打印cpsr
printHex(cpsr);//输出cpsr的值
puts(" ");//输出空格
puts(str);//输出str值
puts("nr");//回车,换行
}
我们打开之前编译过的程序的反汇编文件
里面一定包含了保存恢复
30000084 :
30000084: e1a0c00d mov ip, sp
30000088: e92dd800 stmdb sp!, {fp, ip, lr, pc} //保存 d是减 b是存
3000008c: e24cb004 sub fp, ip, #4 ; 0x4
30000090: e24dd004 sub sp, sp, #4 ; 0x4
30000094: e50b0010 str r0, [fp, #-16]
30000098: e51b3010 ldr r3, [fp, #-16]
3000009c: e2433001 sub r3, r3, #1 ; 0x1
300000a0: e50b3010 str r3, [fp, #-16]
300000a4: e51b3010 ldr r3, [fp, #-16]
300000a8: e3730001 cmn r3, #1 ; 0x1
300000ac: 0a000000 beq 300000b4
300000b0: eafffff8 b 30000098
300000b4: e89da808 ldmia sp, {r3, fp, sp, pc}//恢复,先读后加
上传编译
修改makefile添加文件
objs := start.o led.o main.o uart.o init.o exception.o
all:$(objs)
arm-none-linux-gnueabi-ld -T sdram.lds $^ -o main.elf
arm-none-linux-gnueabi-objcopy -O binary -S main.elf main.bin
arm-none-linux-gnueabi-objdump -D main.elf > main.dis
%.o:%.c
arm-none-linux-gnueabi-gcc -g -nostdlib -c -o $@ $<
%.o:%.S
arm-none-linux-gnueabi-gcc -g -nostdlib -c -o $@ $<
clean:
rm *.bin *.o *.elf *.dis
编译成功烧写
没有输出我们想要的字符串
很多同学想学会如何调试程序
这里我们演示
sdram:
bl print1 //添加print1
/* 故意加入一条未定义指令 */
und_code:
.word 0xdeadc0de /* 未定义指令 */
bl print2 //添加print2,实现这两个函数,来打印
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main /* **跳转, 跳到SDRAM */
halt:
b halt
实现print1 print2这两个打印函数,在uart.c这个文件里
void print1(void)
{
puts("abcnr");
}
void print2(void)
{
puts("123nr");
}
上传代码烧写,发现print1、print2并未执行成功
发现在start.S并未初始化 uart0_init(),删除main.c中的uart0_init()初始化函数
ldr pc, =sdram
sdram:
bl uart0_init /*串口初始化代码添加*/
bl print1
/* 故意加入一条未定义指令 */
und_code:
.word 0xff123456 /* 未定义指令 */
bl print2
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main /* **跳转, 跳到SDRAM */
halt:
b halt
加上uart0_init,再次编译烧写
程序正常运行,print1 print2全部打印,表明未定义指令并未运行,难道这个地址是一个已经定义的地址
打开2440芯片手册,找到ARM指令集
发现竟然是SWI指令,CPU可以识别出来,他不是一条未定义指令
我们得找到一条CPU不能识别的指令,定义为0x03000000
ldr pc, =sdram
sdram:
bl uart0_init
bl print1
/* 故意加入一条未定义指令 */
und_code:
.word 0x03000000 /* 未定义指令 */
bl print2
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main /* **跳转, 跳到SDRAM */
halt:
b halt
编译烧写执行
打印了未定义指令异常CPSR地址,打印了字符串,*后执行main函数
.word 0xdeadcode /* 也是一条未定义指令 只要指令地址对不上上表就是未定义指令*/
我们查看下cpsr是否处于未定义模式
bit[4:0]表示CPU模式 11011,果然处于und模式
我们看看这个程序做了什么事情
.text
.global _start
/*一上电复位,从0地址开始执行
跳到 reset:
做了一系列初始化
当执行到0xdeadc0de这条指令时候,CPU根本就不知道这条指令什么意思
und_code:
.word 0xdeadc0de /* 未定义指令 */
bl print2
让后就发生未定义指令异常,他会把下一条指令的地址保存到异常模式的LR寄存器
/* 执行到这里之前已经发生了很多事情
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x4的地方执行程序
*
* 设置栈 sp是指und的地址
* sp_und未设置, 先设置它
* /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
* lr是异常处理完后的返回地址, 也要保存 */
* 保存现场 */
* 处理und异常 */
* 恢复sp
* cpu就会切换到之前的模式
*/
*/
.text
.global _start
_start:
b reset /* vector 0 : reset */
b do_und /* 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里 */
百检网秉承“客户至上,服务为先,精诚合作,以人为本”的经营理念,始终站在用户的角度解决问题,为客户提供“一站购物式”的新奇检测体验,打开网站,像挑选商品一样简单,方便。打破行业信息壁垒,建构消费和检测机构之间高效的沟通平台