1 未定义中断的原理
1.1 ARM的指令组成
ARM的指令是由32位组成的,是有一定的组成格式的,如果不符合组成格式的话,那就这条指令就无法被识别,就是未定义指令了。指令的[31]~[28] 是条件位,当条件位为1110B时,就表明该指令一定背执行。这里特别指出[31] ~[28]是因为后面的例子种将会使用到。
1.2 执行未定中断的过程
当发现未定义指令时ARM会做什么呢,如同s3c2440学习之路-012-0 异常中断基础知识 的1.4 小节所说的, 程序会自动跳到0x4的位置去执行代码。
具体的执行过程如下:
执行某条命令,发现不符合ARM的命令格式,产生未定义异常
发生异常时硬件的处理,即进入异常
2.1将返回地址保存在LR(R14)寄存器
2.2将CPSR复制到SPSR(SPSR 就是专门弄来备份CPSR的)
2.3设置CPSR的模式位(设置CPSR的bit0~bit4)
2.4设置PC值为0x4(对应未定义异常向量的地址)
发生异常时软件的处理,即处理异常然后回到异常发生时的位置继续处理
3.1 设置SP(因为需要通过压栈来保存寄存器和代码跳转)
3.2 保存现场(将通用的寄存器保存起来)
3.3 将SPSR赋值给CPSR
3.4 清除中断标志位(未定义异常无中断标志可清除)
3.5 恢复现场(将通用的寄存器恢复回去)
3.6 将LR直接赋值给PC(这里不需要做差值)
我们把重点放在软件的处理上,接下来通过源码来分析,这样可以更好的体会到未定义中断的处理过程。
2 源码分析
2.1 进入未定义中断
start.s
ldr pc, =sdram_addr
sdram_addr:
bl uart0_init
bl print_hello
.word 0xdeadc0de
bl print_hello
ldr pc, =main /* abs jump to main */
loop:
b loop
ldr pc, =sdram_addr 是为了让代码跳到SDRAM上去执行,如果不清楚请看s3c2440学习之路-011代码重定位
接下来就是初始化串口,然后执行print_hello函数,这个函数非常简单,就是打印"hello"。
void print_hello(void)
{
printf("n");
printf("hellon");
}
接下来就是重头戏,.word 0xdeadc0de, 是一条未定指令(0xdeadc0de,C零DE, 不是C欧DE, 有点像Dead Code 死代码),它的组成不符合ARM指令格式,因此会产生未定义中断。回顾前面所说的,最终硬件会把PC赋值成0x4, 程序会跑到0x4的地址去执行。
2.2 未定义中断的软件处理
start.s
.text
.global _start
_start:
b _reset
ldr pc, _undef_addr
_undef_addr: .word _undef
_undef:
/*set bank sp, sdram end address is 0x34000000
because sdram size is 64M,
and sdram start address is 0x30000000 */
ldr sp, =0x34000000
/* store register */
stmdb sp!, {r0-r12, lr}
mrs r0, cpsr
ldr r1, =undef_string
bl printf_undef
mov r0, #4
bl led_off
/* resume register */
ldmia sp!, {r0-r12, pc}^ /*^ means copy spsr to cpsr */
undef_string:
.string "Undefinf Excption!"
.align 4
_reset:
/* stop watch dog */
ldr r0, =0x53000000
mov r1, #0
str r1, [r0]
程序开始会执行2条指令,第一条是b reset, 所处的地址是0x0, 也就是上电后自动执行的第一条指令。第二条指令是ldr pc, _undef_addr, 所处的地址是0x4, 也就是发生未定义中断时硬件会自动跳到这里的地址。要弄懂这条命令的意思,就需要弄懂ldr 汇编指令的2种用法。
ldr r0, =0xA
ldr r0, 0xB
上面2条ldr指令,一条是带"=",一条不带"="。 第1条指令的意思是r0=0xA, 是直接赋值的意思,而第2条指令是获取0xB地址上的值,再赋值给r0。如果把0xB当做指针的话,第2条指令就等价于r0=*0xB。一个是直接赋值,一个是取它地址里面的值,在赋值。
在回到ldr pc, _undef_addr, 先获取_undef_addr地址处的数值,在赋值给pc。通过反汇编查看可知,_undef_addr的值为0x30000008, 而0x30000008地址上的值为0x3000000C ,因此ldr pc, _undef_addr 就等价于ldr pc, =0x3000000C。
(这里起始地址为0x30000000是因为链接脚本的原因, 实际的存储地址需要减去0x30000000)
代码跳到0x3000000C的位置,也就是_undef: 标号的位置。接下来代码就会按照之前所说的流程来处理。
2.2.1 设置SP
ldr sp, =0x34000000, 重新设置栈,原因有2个:
在发生未定义中断前,ARM就进入了未定义模式,此时sp(r13)寄存器是此模式下私有的寄存器, 不理解请看 s3c2440学习之路-012-0 异常中断基础知识
后续需要跳转到C函数和压栈操作,这些都依赖sp寄存器
2.2.2 保存现场
stmdb sp!, {r0-r12, lr}, stmdb是用来存储多个寄存器的指令。意思是以sp的数值为基础(0x340000000),依次把r0-r12, lr寄存器入栈保存起来。如果不懂麻烦上网查查,这里不做过深的解释。
2.2.3 打印数值和点亮led灯
mrs r0, cpsr
ldr r1, =undef_string
bl printf_undef
mov r0, #4
bl led_off
/* resume register */
ldmia sp!, {r0-r12, pc}^ /*^ means copy spsr to cpsr */
undef_string:
.string "Undefinf Excption!"
.align 4
通过mrs命令把cpsr 的数值读给r0, 把字符串"Undefinf Excption!" 传递给r1, 然后调用printf_undef 函数。
printf_undef 函数就是先打印传进来的字符串,然后输出cpsr的数值。
如果不清楚为什么要把值传给r0, r1 请看s3c2440学习之路-003 汇编给C传参数 点亮不同led灯
void printf_undef(unsigned int cpsr_status, char *string)
{
printf("%sn", string);
printf("cpsr:0x%xn", cpsr_status);
}
最终的实验结果就是, 先打印hello, 然后输出Undefinf Excption! ,cpsr的数值为0x600000db
0xdb=1101 1011b, 最低5位为11011b 也就是Undefinded模式。从这里可以看出,ARM确实进入了未定义模式。
2.2.4 恢复现场
ldmia sp!, {r0-r12, pc}^, 一条指令就搞定了。ldmia 对应前面的stmdb 命令,一个是把寄存器入栈存起来,一个是把寄存器出栈取出来。这里有2小点要注意
保存的时候是stmdb sp!, {r0-r12, lr}, 而取出时是ldmia sp!, {r0-r12, pc}^, 最后面一个是保存lr, 一个是取出pc, 就等价于把lr 赋值给pc了
ldmia sp!, {r0-r12, pc}^ 最后还有一个"^" 符号, 意思是把spsr赋值给cpsr
这么简单的一条指令就完成了3个动作:
把之前保存的寄存器r0-r12恢复
spsr赋值给cpsr
lr 赋值给pc ,代码就回到了发生未定义中断前的位置了
2.3 总结
通过2.2 节 可以看出,对于未定义中断,整个软件部分的处理流程就如同1.2 小节说所的。
这里还有一个小疑问,问什么程序的一开始就是2条跳转指令呢?
2条跳转指令的地址分别位于0x0, 0x4, 这是ARM在发生复位操作和未定义中断时会去访问的地址。这里拿未定义中断来说,软件的处理有很多步骤需要完成,因此一条指令是无法完成的,所以在0x4的位置直接执行跳转ldr pc, _undef_addr, 跳转到其地方来完成,避免占用到0x0~0x1C的位置。(0x0 ~ 0x1C是ARM不同异常时的向量地址,不过目前我的程序只需要0x0 和0x4 不被占用)
因此,2440 uboot 的开头就是一堆的跳转指令,而且是按照下面的异常向量表来写的。
2440 uboot的start.S 开头部分代码
3 遗留问题
3.1 bl print_hello
sdram_addr:
bl uart0_init
bl print_hello
.word 0xdeadc0de
bl print_hello
ldr pc, =main /* abs jump to main */
loop:
b loop
在执行 .word 0xdeadc0de 前先执行了bl print_hello。但是发现如果把bl print_hello 去掉后,就不会出现未定异常了,换句话说.word 0xdeadc0de 指令被忽视了。
由此做了一个小测试,在word 0xdeadc0de 后面打印了cpsr的值,分2种情况
去掉106 行的bl print_hello,bl printf_cpsr的值为:0x200000d3
保留106 行的bl print_hello,bl printf_cpsr的值为:0x600000d3
唯一的不同就是最高的拿一个字节,一个是0x2, 一个是0x6。查看手册可以得到, 0x2表示进为,0x6 表示溢出位,为何0x2不会被执行,而0x6会被执行,这里暂时不清楚,不过区别点就在这里。我们这里的原因就是word 0xdeadc0de 指令被忽视了,没有执行。
为了保证指令被执行,需要将0xdeadc0de 修改为0xeeadc0de , 这样最高位就是1110b, 一定会被执行。这样即使去掉106行的去掉106 行的bl print_hello, .word 0xeeadc0de 也一定会被执行,一定会产生未定义中断。
上一篇:s3c2440学习之路-002 C语言点亮led
下一篇:S3c2440代码重定位详解2---链接脚本的引入与简单测试
推荐阅读最新更新时间:2024-11-09 08:12
推荐帖子
- 智能哑铃
- 本帖最后由paulhyde于2014-9-1509:11编辑智能哑铃智能哑铃本帖最后由paulhyde于2014-9-1509:11编辑顶顶!!!!!!!!!!!!!
- 呱呱 电子竞赛
- 大二小学渣求助,在线等回复
- 各位大神,本人大二小学渣妹纸一枚,现有比赛,时间紧迫,求解单片机知识我想用一个单片机控制三个LED灯泡,LED灯泡应该接在单片机的哪个IO口上,还需要加三极管之类的东西么?求助求助,在线等大二小学渣求助,在线等回复具体看怎嘛弄,p。0.1.2.3都可以,一般不用放三极管,不过要加保护电阻wu好的。。。谢谢。。。太爱你了回复沙发青龙19的帖子注意,如果是51单片机的话,最好用低电平驱动LED,LED的阴极接IO口。你的LED灯泡是多大功率的?你用的什么单片机呢?AT8
- 奋进的小学渣 51单片机
- 网络技术基础知识(八)~~网络分类
- 什么是网络?什么是Internet?简单的来讲,网络就是在一定的区域内两个或两个以上的计算机以一定的方式连接,以供用户共享文件、程序、数据等资源。Internet,即全球信息网(WorldWideWeb,简称WWW),是基于超文本(Hypertext)的信息检索工具,它通过超链接把世界各地不同Internet节点上的相关的信息有机地组织在一起,用户只需发出检索请求,它就能自动地进行相应的定位,找到相应的检索信息。下面就几种常见的网络类型及分类方法作简单的介绍。
- mdreamj RF/无线
- 基于MSP430单片机的称重式液位仪的设计
- 基于MSP430单片机的称重式液位仪的设计时间:2011-10-0618:06:36来源:作者:1引言 液位测控仪是属于智能化仪器仪表的一种(指采用了微处理器的仪器仪表),其发展始于70年代[1]。它是一种集测量与控制于一体的智能化产品,适用于石油化工、冶金、电力、制药、环保等行业中各种介质的液位测量。本仪器主要针对罐体内液体进行测量并能计算其重量,适用于对各种液态物质进行静态和动态测量与监控,并具有超限报警和主-从站模式联网功能。 2系统设计方案 2.1
- oyzw111 微控制器 MCU
- TI新出了个带以太网的芯片SimpleLink™ MSP432E4系列
- TI新出了个带以太网的芯片SimpleLink™MSP432E4EthernetmicrocontrollersMSP432E401YTheSimpleLinkMSP432E401YArm®Cortex®-M4Fmicrocontrollersprovidetopperformanceandadvancedintegration.Theproductfamilyispositionedforcost
- damiaa TI技术论坛
- 【DigiKey创意大赛】家庭共享智能药盒06+作品提交
- 家庭共享智能药盒作者:oet一、作品简介以往的智能药盒主要是针对单个用户设计,实际应用中,随着老龄化越发严峻,家中有两位甚至更多老人长期吃药,维持血压,血糖水平的需求越来越多,单用户药盒就不太适用了。为了解决这个痛点,我设计这个家庭共享智能药盒,支持多个用户使用。本方案采用STM32H7B3I-DK作为主控,使用OpenMV进行人脸识别,配合屏幕显示和语音提示,做成自动识别用户,主动显示药品格子号,并点亮药品格子指示灯,让用户尽量少做选择。该作品还安装了BME280传感器,用
- oet DigiKey得捷技术专区
设计资源 培训 开发板 精华推荐
- AD8532ARUZ-REEL 单电源直接接入调制解调器的典型应用
- LTC2915、具有容限控制的 1.5V 电源监视器
- LT1303、3 节电池至 3.3V/200mA SEPIC 转换器
- 热插拔电路符合 InfiniBand 规范
- 具有高阻抗输入源的 LT3976IUDD 4V 降压转换器的典型应用电路
- AD8657ARMZ-R7比较器A典型应用电路
- CH340N-USB转TTL串口烧录器
- LTC2257IUJ-12、12 位、40Msps 超低功耗 1.8V ADC 的典型应用电路
- 三端固定稳压器,具有输出控制的低压精密可调并联稳压器的典型应用
- DC1525A-H,基于 LTC2174-12、12 位、105Msps、1.8V 四路串行 ADC 的演示板
- 【原创】请问谁有用MSP430实现读DS18B20的程序?
- Xilinx spartan6嵌入式套件现场培训资料下载
- ATSAMD10 Xplained Mini和ATmega168PB Xplained Mini 简单对比
- 89c52数码管频率计(源程序+仿真)
- 开了个专门谈论技术的Google网上论坛,希望同志们去踩踩
- Application Note for Phase shifter, PS-MCM-1.9G
- 晒WEBENCH设计的过程+DC8-16V转5V/1A车载充电器的设计过程
- TI电池管理方案在智能锁上的应用
- usb驱动问题!!!dispatch_level在callusbdi下不可用!!急急急!!
- 基于安卓/苹果的物品防盗管家,用于对随身箱包和贵重物品进行管理