• 首页
  • 中国
  • S3C2440裸机------异常与中断__und异常模示程序示例

S3C2440裸机------异常与中断__und异常模示程序示例

2023-11-01 40浏览
百检网是一家专业的第三方检测平台,汇聚众多拥有权威资质的第三方检测机构为你提供一站式的检测服务,做检测就上百检网。百检网让检测从此检测,一份报告全国通用,专业值得信赖。

1.异常向量表

我们先看一下芯片手册里面的异常向量表,

2.代码流程

我们在重定位的第10个程序的基础上进行修改。

我们要在start.S的前面增加跳转到相应异常向量表的代码,并且要增加代码,当发生未定义指令异常时候跳转到该代码块进行保存现场、处理未定义异常以及恢复现场的工作,然后在下面故意写一条不能被识别的指令。

2.1增加异常向量表代码

**在start.S的前面增加相应的跳转指令,当发生异常时会根据这里的跳转指令跳转到相应的地方,代码如下。

_start:

b reset /* vector 0 : reset */

b do_und /* vector 4 : und */

2.2设置栈

进入异常模式之后,使用的异常模式下的栈,所以我们要在处理异常之前设置一下异常模式的栈,我们随便找一块没有用到的内存就可以了,这里我们让他指向64MSDRAM的*高地址0x34000000。

/* sp_und未设置, 先设置它 */

ldr sp, =0x34000000

2.3 保存现场

由于r0-r12都有可能被修改,因为我们要把r0-r12都保存一下,另外当发生异常时,当前被中断的地址回报存在LR寄存器里面,所以我们也要把LR寄存器保存一下,这里stmdb只是stm decrease before,也就是先减后存。

/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */

/* lr是异常处理完后的返回地址, 也要保存 */

stmdb sp!, {r0-r12, lr}

2.4 处理异常

这里处理未定义异常的的工作我们通过编写一个C函数实现,我们新建一个exception.c,在里面编写我们的处理函数,exception.c内容如下:

#include "uart.h"

void printException(unsigned int cpsr, char *str)

{

puts("Exception! cpsr = ");

printHex(cpsr);

puts(" ");

puts(str);

puts("nr");

}

这个函数由两个参数,所以我们在start.S中调用这个函数时要传给他两个参数,在汇编代码中定义字符串可以使用.ascii和.string,他们两个的区别在于,用.ascii定义的字符串变量不会再末尾自动加上结束符.

/* 处理und异常 */

mrs r0, cpsr

ldr r1, =und_string

bl printException

/* 恢复现场 */

ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

und_string:

.string "undefined instruction exception"

2.5 恢复现场

把r0-r12的值从栈中读出来,并且并原来保存在LR寄存器中的值赋值给PC,ldmia是指ldm increase after,也就是 先读后加。

/* 恢复现场 */

ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

2.6 故意加入一条未定义指令

我们看一下2440的芯片手册,找到ARM指令集,只要是下表中的指令都是合法的指令。我们需要定义一条下表中不支持的非法指令,或者直接用0xdeadc0de表示一条未定义指令。

/* 故意加入一条未定义指令 */

und_code:

.word 0xdeadc0de /* 未定义指令 */

3.代码

3.1start.S

下面我们看一下修改后的完整的start.S。

.text

.global _start

_start:

b reset /* vector 0 : reset */

b do_und /* vector 4 : und */

do_und:

/* 执行到这里之前:

* 1. lr保存有被中断模式中的下一条即将执行的指令的地址

* 2. SPSR保存有被中断模式的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

bl print1

/* 故意加入一条未定义指令 */

und_code:

.word 0xdeadc0de /* 未定义指令 */

bl print2

//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */

ldr pc, =main /* **跳转, 跳到SDRAM */

halt:

b halt

3.2 exception.c

#include "uart.h"

void printException(unsigned int cpsr, char *str)

{

puts("Exception! cpsr = ");

printHex(cpsr);

puts(" ");

puts(str);

puts("nr");

}

4.优化

我们把前面异常向量表的跳转指令改为**跳转,这样保证如果是nandflash,并且代码大于4K,那么我们后面的bl printException也不会出错,

_start:

b reset /* vector 0 : reset */

ldr pc, und_addr /* vector 4 : und */

这样修改之后还有一个问题,我们看一下反汇编,

它去300000c0这个地方去取值,

然后c0这个地方存在的30000008,30000008就是下面这条指令的地址。

其实就是do_und指令的地址保存到内存中,然后ldr pc, und_addr会去内存中找到这个值,然后付给pc, 那么他去内存的那个地方找这个值呢,一般由编译器决定,通常会放在汇编文件的*后面,我们这个start.S非常小,如果这个start.S非常大,那么他有可能会去4K之外的地方取值,所以我们能不能指定让他去前面的内存去读这个值呢,因为我们担心这个汇编文件很大的话,有可能超过4K,如果你的板子是nand启动,那么在4K之外就没有办法读到值。修改如下:

_start:

b reset /* vector 0 : reset */

ldr pc, und_addr /* vector 4 : und */

und_addr:

.word do_und

同样下面的代码在重定位之后,我们就应该跳转到SDRAM去执行代码,所以增加ldr pc, =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

修改后的start.S如下:

.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

修改后的程序整体执行过程如下:


百检网秉承“客户至上,服务为先,精诚合作,以人为本”的经营理念,始终站在用户的角度解决问题,为客户提供“一站购物式”的新奇检测体验,打开网站,像挑选商品一样简单,方便。打破行业信息壁垒,建构消费和检测机构之间高效的沟通平台