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

发布者:csZhou最新更新时间:2021-10-12 来源: eefocus关键字:und 手机看文章 扫描二维码
随时随地手机看文章

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

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

关键字:und 引用地址:S3C2440裸机------异常与中断__und异常模示程序示例

上一篇:S3C2440裸机------异常与中断__swi异常模示程序示例
下一篇:S3C2440裸机------异常与中断__Thumb指令集程序示例

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved