S3C2440—11.und异常
1 未定义指令
先看一下ARM中指令的格式:
注意:这个表的最上面的对应的位是从高到低的,所以0Xdeadc0de 就是一条未定义指令(方便易懂)
2 中断向量表
未定义异常,首先要产看中断向量表:
在板子上电之后,CPU从0地址开始执行,首先执行复位Reset操作,而且触发异常之后会跳转至相应的地址去进入异常处理程序,所以我们要在0地址开始按照中断向量表编写相应的异常处理程序的指引,如下:
.text
.global _start
_start:
/* 异常向量表 */
bl reset /* 0X0 Reset 上电复位,从0地址开始执行程序,依次:关闭看门狗、配置时钟系统、初始化sdram、拷贝代码到sdram(重定位)、清除.bcc段、进入mian函数 */
ldr pc, =und_addr /* 0X4 Undefined instruction */
// bl do_swi /* 0X8 Software Interrupt */
// bl do_ap /* 0XC Abort(prefetch) */
// bl do_ad /* 0X10 Abort(data) */
// bl do_re /* 0X14 Reserved */
// bl do_irq /* 0X18 IRQ */
// bl do_fiq /* 0X1C FIQ */
3 设置一个未定义指令
人为引入一个未定义指令,如果遇到未定义的指令的时候,比如如下的指令0Xeeadc0de:(改正:将0Xdeadc0de改为0Xeeadc0de,因为0Xdeadc0de实际上是一条 条件执行语句,只有触发条件才会执行下面的代码)
CPU执行到这里就会取指令,发现这是一个未定义指令,就会自动触发und异常,CPU就会在中断向量表中找到und的处理程序地址映射,通过跳转指令去执行und的处理程序。
4 调用C函数
und的处理函数可以用C语言写好,在汇编中调用即可,不过要注意参数的传递:
C函数如示:
void Und_Process( unsigned int cpsr )
{
puts("\n\rEnter Und_Process!!!\n\rCPSR is:");//打印提示信息,进入und处理程序
printHex( cpsr );//输出CPSR寄存器的值,确认当前的模式
puts("\n\r");
}
und异常处理函数就是打印一些信息。
汇编中参数传递如下:
mrs r0, cpsr /* mrs读出寄存器的值,通过r0寄存器向下面的函数传参 */
==注意:==用C写的und处理函数,就要用到栈!!!虽然在汇编中我们设置过栈指针sp了(上电后处于管理模式,所以设置的是管理模式下的栈指针sp),但是对于异常模式,每个异常模式都有自己的栈指针!!!如下:
所以进入und处理程序后要先设置sp_und栈指针,为调用的C处理函数分配空间,随便指向一个没有用过的空间就可以了 比如
ldr sp, =0x34000000 /* 指向了64M SDRAM的最高地址 */
这样就可以调用C函数了!
5 UND异常处理程序
有了und异常处理函数,就可以在汇编中调用处理函数,但是汇编中的异常处理程序还有其他操作。
汇编中的und异常处理程序如下:
und_addr:
.word do_und
/* und异常处理,进入异常前,硬件完成的事情:将CPSR拷贝到SPSR,将被中断指令的地址存储在lr中 */
do_und:
ldr sp, =0x34000000 /* und的栈指针,指向64M 的SDRAM的最高地址,为C函数分配空间 */
stmdb sp!, {r0-r12,lr} /* 保存现场 */
mrs r0, cpsr /* mrs读出寄存器的值,通过r0寄存器向下面的函数传参 */
bl Und_Process
ldmia sp!, {r0-r12, pc}^ /* 恢复现场,注意:一定要加!来保存sp的改变 */
在触发und异常后,进入异常程序处理之前,硬件会进行一些操作:
1.将CPSR拷贝到SPSR中
2.CPSR中的M4-M0被自动设置为11011,进入und模式
3.将被中断指令的地址存储在lr寄存器中
4.程序跳到中断向量表0X4的地址(und)去执行中断异常处理程序
在异常处理程序中,要进行的操作是:
1.设置栈,通过sp_und来设置
2.保存现场,包括r0~r12寄存器、lr寄存器,保存lr也是必须的,因为lr中的是异常处理完之后的返回地址
3.调用C处理函数
4.恢复现场,利用ldmia sp!, {r0-r12,pc}^ 恢复各个寄存器的值,将lr寄存器的值赋值给pc寄存器(有待考证,ia是先 后 ),^顺便把SPSR中的值恢复到CPSR中
6 汇编源码
.text
.global _start
_start:
/* 异常向量表 */
bl reset /* 0X0 Reset 上电复位,从0地址开始执行程序,依次:关闭看门狗、配置时钟系统、初始化sdram、拷贝代码到sdram(重定位)、清除.bcc段、进入mian函数 */
ldr pc, =und_addr /* 0X4 Undefined instruction */
// bl do_swi /* 0X8 Software Interrupt */
// bl do_ap /* 0XC Abort(prefetch) */
// bl do_ad /* 0X10 Abort(data) */
// bl do_re /* 0X14 Reserved */
// bl do_irq /* 0X18 IRQ */
// bl do_fiq /* 0X1C FIQ */
und_addr:
.word do_und
/* und异常处理,进入异常前,硬件完成的事情:将CPSR拷贝到SPSR,将被中断指令的地址存储在lr中 */
do_und:
ldr sp, =0x34000000 /* und的栈指针,指向64M 的SDRAM的最高地址,为C函数分配空间 */
stmdb sp!, {r0-r12,lr} /* 保存现场 */
mrs r0, cpsr /* mrs读出寄存器的值,通过r0寄存器向下面的函数传参 */
bl Und_Process
ldmia sp!, {r0-r12, pc}^ /* 恢复现场,注意:一定要加!来保存sp的改变 */
.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] /* 恢复原来的值 */
/* 代码重定位的时候,首先初始化SDRAM,然后拷贝代码,然后清除.bss段(防止内存访问出错),最后执行main函数,main就在重定位的SDRAM中去执行 */
bl sdram_init
//bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */
/* 重定位text, rodata, data段整个程序 */
bl copy2sdram
/* 清除BSS段 */
bl clean_bss
ldr pc, =UartInit
UartInit:
bl uart0_init
/* 引入und指令,触发und异常 */
und_test:
.word 0Xeeadc0de
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main /* 绝对跳转, 跳到SDRAM */
halt:
b halt
7 注意点
lr与pc
发生异常时,当前被中断的地址会被硬件自动保存在 lr 寄存器中,可以对应表查看要存储在lr寄存器的地址的值:
可以查到,und异常发生时,lr寄存器的值是PC+4(ARM指令集)
保存现场
保存现场,也就是保存寄存器r0~r12都要保存(进入异常模式后,硬件会把CPSR保存到SPSR中)
所以在保存r0~r12的时候连lr寄存器也一起保存了:
stmdb sp!, { r0-r12, lr }
在恢复现场的时候进行相反的操作:
ldmia sp!, { r0-r12, pc }^ /* ia:先读后加 ^会把spsr的值恢复到cpsr中 */
中断向量表的跳转
跳转的时候使用 ldr pc, =do_und(防止因为重定位出错),下面bl Und_Process的C函数也就在SDRAM中了,就是重定位之后的运行地址。
实际上 ldr pc, =do_und是一个伪指令,是把do_und的值(也就是und异常处理程序首地址)存储在内存中,然后去读内存,再给pc赋值。这个内存经常是紧跟在汇编文件后面的,但是当汇编文件过大时(超过4K,在NAND启动的时候就不会读到4K地址外的值),就可能引起错误。
可以间接地将do_und的值放在汇编文件之中:
und_addr:
.word do_und
这样,在中断向量表中只需要:
ldr pc. = und_addr
如示:
程序执行顺序
程序的内存执行顺序就是:
1.0地址 b reset
2.0X4地址 ldr pc, =und_addr
3.接下来的内存空间就是是重定位的相关代码
4.ldr pc, =sdram跳转到重定位的代码中执行程序(所有代码全部复制到SDRAM 0X30000000)
5.在重定位后的代码中触发und,发生异常
6.发生异常后,CPU强制跳转到0X4的中断向量表中去执行!!!注意,这里又跳回重定位前的程序0X4处执行
7.还是在原程序中 找到ldr pc, =und_addr 跳转到重定位(SDRAM)的代码中执行中断异常程序
8.执行完中断异常程序后,返回现场继续执行
问题
只有在und之前加上 bl print1 才可以正常执行程序,为什么???
已经解决:
原因是那条未定义指令:0Xdeadc0de 这实际上是一条条件执行语句,就是因为 bl print1恰好符合条件才导致的必须加上 bl print1,把未定义指令改为0Xeeadc0de就可以了。
问题链接地址:https://mp.weixin.qq.com/s/lJ3hzWPVt1HcR9L2cK0LoA
S3C2440—11.und异常的更多相关文章
- Java核心技术卷一基础知识-第11章-异常、断言、日志和调试-读书笔记
第11章 异常.断言.日志和调试 本章内容: * 处理错误 * 捕获异常 * 使用异常机制的技巧 * 使用断言 * 日志 * 调试技巧 * GUI程序排错技巧 * 使用调试器 11.1 处理错误 如果 ...
- python之最强王者(11)——异常(exception)
1.Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误.你可以使用该功能来调试python程序. 异常处理: 本站Python教程会具体介绍. ...
- A Byte of Python 笔记(11)异常:try..except、try..finally
第13章 异常 当你的程序中出现某些 异常的 状况的时候,异常就发生了. 错误 假如我们把 print 误拼为 Print,注意大写,这样 Python 会 引发 一个语法错误. 有一个SyntaxE ...
- JAVA基础知识总结11(异常)
异常: 就是不正常.程序在运行时出现的不正常情况.其实就是程序中出现的问题.这个问题按照面向对象思想进行描述,并封装成了对象.因为问题的产生有产生的原因.有问题的名称.有问题的描述等多个属性信息存在. ...
- 第11章 Java异常与异常处理
1.Java异常简介 1.什么是异常异常出现的时候代码会无法正常运行下去,会产生各种问题2.捕捉异常的作用提早发现异常,方便查找问题,并给出解决方法3.Java中的异常1.Java中所有不正常的类都是 ...
- Linux Kernel Oops异常分析
1.PowerPC小系统内核异常分析 1.1 异常打印 Unable to handle kernel paging request for data at address 0x36fef31eFa ...
- C++11异常处理 noexcept
1.简介 在C语言中,如果程序的运行出现异常.错误,我们想提供方案处理这些异常时,我们面临许多问题,如: (1)C语言没有提供统一(标准)的方式来处理错误: (2)无法保证错误会被正确的处理: (3) ...
- JavaEE基础(十九)/异常和File
1.异常(异常的概述和分类) A:异常的概述 异常就是Java程序在运行过程中出现的错误. B:异常的分类 通过API查看Throwable Error 服务器宕机,数据库崩溃等 Exception ...
- [core java学习笔记][第十一章异常断言日志调试]
第11章 异常,断言,日志,调试 处理错误 捕获异常 使用异常机制的技巧 使用断言 日志 测试技巧 GUI程序排错技巧 使用调试器 11.1 处理错误 11.1.1异常分类 都继承自Throwable ...
随机推荐
- php弱类型比较
前言:今天XCTF题目中出现了弱类型比较,特别过来记录一下, 0x01: == 是弱类型比较,两个不同类型比较时,会自动转换成相同类型后再比较值 ===是强比较,需要比较值和类型 0x02: 看下图案 ...
- Spring中如何使用自定义注解搭配@Import引入内外部配置并完成某一功能的启用
文章背景 有一个封装 RocketMq 的 client 的需求,用来提供给各项目收.发消息,但是项目当中常常只使用收或者发消息的单一功能,而且不同的项目 group 等并不相同而且不会变化,可以在项 ...
- centos7 U盘安装及Raid划分的完整流程
目录 一.Centos7的新特性: 二.安装方法与准备工作(U盘镜像) 1. 安装方法介绍 2. Centos iso 常用镜像下载地址: 3. UltraISO制作U盘系统镜像 3.1 准备工作: ...
- JS请求节流
少废话,撸代码.欧耶! 1.节流器 // 对函数进行 节流 function throttle (fn, interval = 500) { let timer = null; let firstTi ...
- JM操作数据库
[前言] 为什么要去直连数据库,去操作数据库? 因为在我们做自动化的时候,或者在大批量准备数据的时候,自动化的时候有时候会生成很多条页面上,接口上无法删除的数据,那么就很有很多的测试数据遗留在系统上, ...
- CF1329F题解
能发现: 1.输出序列与掉落顺序没有任何关系(因为单调性不会被改变). 2.输出的序列 \(h_i\) 最多有一组 \(h_i=h_{i+1}\). 对 2 的证明: 当 \(h_{i+1}\) 与 ...
- 超详细!Vuex手把手教程
目录 1,前言 2,Vuex 是什么 3,5大属性说明 4,state 4.1 直接访问 4.1 使用mapState映射 5,getters 5.1 先在vuex中定义getters 5.2 直接获 ...
- informix 数据库锁表分析和解决方法
一.前言 在联机事务处理(OLTP)的数据库应用系统中,多用户.多任务的并发性是系统最重要的技术指标之一.为了提高并发性,目前大部分RDBMS都采用加锁技术.然而由于现实环境的复杂性,使用加锁技术又不 ...
- C++第三十六篇 -- 为第一个驱动程序进行调试
工具是VMware12+Win10+VS2017+WDK1809 https://blog.csdn.net/qq_21763381/article/details/83242916 首先分清楚主计算 ...
- Android系统编程入门系列之界面Activity响应多元的属性动画
在响应丝滑动画一篇文章中,分别介绍了作用于普通视图.绘制视图的绘制对象.和界面这三种对象的动画效果,但是都有一些使用的局限性.比如这些动画都只是以屏幕上绘制更新的方式绘制动画,并没有真实改变作用对象的 ...