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异常的更多相关文章

  1. Java核心技术卷一基础知识-第11章-异常、断言、日志和调试-读书笔记

    第11章 异常.断言.日志和调试 本章内容: * 处理错误 * 捕获异常 * 使用异常机制的技巧 * 使用断言 * 日志 * 调试技巧 * GUI程序排错技巧 * 使用调试器 11.1 处理错误 如果 ...

  2. python之最强王者(11)——异常(exception)

    1.Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误.你可以使用该功能来调试python程序. 异常处理: 本站Python教程会具体介绍. ...

  3. A Byte of Python 笔记(11)异常:try..except、try..finally

    第13章 异常 当你的程序中出现某些 异常的 状况的时候,异常就发生了. 错误 假如我们把 print 误拼为 Print,注意大写,这样 Python 会 引发 一个语法错误. 有一个SyntaxE ...

  4. JAVA基础知识总结11(异常)

    异常: 就是不正常.程序在运行时出现的不正常情况.其实就是程序中出现的问题.这个问题按照面向对象思想进行描述,并封装成了对象.因为问题的产生有产生的原因.有问题的名称.有问题的描述等多个属性信息存在. ...

  5. 第11章 Java异常与异常处理

    1.Java异常简介 1.什么是异常异常出现的时候代码会无法正常运行下去,会产生各种问题2.捕捉异常的作用提早发现异常,方便查找问题,并给出解决方法3.Java中的异常1.Java中所有不正常的类都是 ...

  6. Linux Kernel Oops异常分析

    1.PowerPC小系统内核异常分析 1.1  异常打印 Unable to handle kernel paging request for data at address 0x36fef31eFa ...

  7. C++11异常处理 noexcept

    1.简介 在C语言中,如果程序的运行出现异常.错误,我们想提供方案处理这些异常时,我们面临许多问题,如: (1)C语言没有提供统一(标准)的方式来处理错误: (2)无法保证错误会被正确的处理: (3) ...

  8. JavaEE基础(十九)/异常和File

    1.异常(异常的概述和分类) A:异常的概述 异常就是Java程序在运行过程中出现的错误. B:异常的分类 通过API查看Throwable Error 服务器宕机,数据库崩溃等 Exception ...

  9. [core java学习笔记][第十一章异常断言日志调试]

    第11章 异常,断言,日志,调试 处理错误 捕获异常 使用异常机制的技巧 使用断言 日志 测试技巧 GUI程序排错技巧 使用调试器 11.1 处理错误 11.1.1异常分类 都继承自Throwable ...

随机推荐

  1. 深入理解Java容器——HashMap

    目录 存储结构 初始化 put resize 树化 get 为什么equals和hashCode要同时重写? 为何HashMap的数组长度一定是2的次幂? 线程安全 参考 存储结构 JDK1.8前是数 ...

  2. MySQL | Xtrabackup 的简介

    Xtrabackup 简介 Xtrabackup是由Percona开发的一个开源软件,可实现对InnoDB的数据备份,支持在线热备份(备份时不影响数据读写). Xtrabackup有2款主要工具,xt ...

  3. (精)题解 guP4878 [USACO05DEC] 布局

    差分约束模版题 不过后三个点简直是满满的恶意qwq 这里不说做题思路(毕竟纯模板),只说几个坑点: 1. 相邻的两头牛间必须建边(这点好像luogu没有体现),例如一组数据: 4 1 1 1 4 10 ...

  4. C语言:编译具体过程及隐藏

    对于平常应用程序的开发,很少有人会关注编译和链接的过程,因为我们使用的工具一般都是流行的集成开发环境(IDE),比如 Visual Studio.Dev C++.C-Free 等.这些功能强大的 ID ...

  5. LeetCode解题记录(双指针专题)

    1. 算法解释 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务.也可以延伸到多个数组的多个指针. 若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域 ...

  6. 案例分享:Qt+Arm基于RV1126平台的内窥镜软硬整套解决方案(实时影像、冻结、拍照、录像、背光调整、硬件光源调整,其他产品也可使用该平台,如视频监控,物联网产品等等)

    自研产品系列方案   1. 基于瑞芯微的 RV1126 芯片平台:  2. 外接 USB 摄像头(OV9734. OV6946.OV2740 等 UVC 模块)作为图像输入源:  3. 可通过 LED ...

  7. 在 .NET 中创建对象的几种方式的对比

    在 .net 中,创建一个对象最简单的方法是直接使用 new (), 在实际的项目中,我们可能还会用到反射的方法来创建对象,如果你看过 Microsoft.Extensions.DependencyI ...

  8. maven添加阿里云镜像

    apache的maven服务器国内访问太慢了,用阿里的镜像会好很多. 1.maven的配置文件有两个,安装目录conf文件夹下settings.xml和用户目录.m2文件夹下的settings.xml ...

  9. Python urllib翻译笔记一

    22.5.urllib- URL处理模块urllib 是一个收集几个模块以处理URL的包: urllib.request 用于打开和阅读URL urllib.error 包含由urllib.reque ...

  10. 构建前端第2篇之--ESLint 配置

    张艳涛 写于2021-1-19 报错: http://eslint.org/docs/rules/space-before-function-paren Missing space before fu ...