s3c2440学习之路-012-1 Undefined未定义中断

发布者:凌晨2点369最新更新时间:2021-10-18 来源: eefocus关键字:s3c2440  Undefined 手机看文章 扫描二维码
随时随地手机看文章

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  Undefined 引用地址:s3c2440学习之路-012-1 Undefined未定义中断

上一篇:s3c2440学习之路-002 C语言点亮led
下一篇:S3c2440代码重定位详解2---链接脚本的引入与简单测试

推荐阅读最新更新时间:2024-11-09 08:12

linux-2.6.24.3下移植SD/MMC到S3C2440上的全历程
1.起首下载2.6.24.3内核源码。其他内核不包管可以。移植部门就不讲了,假如不会参考我的其他文章。我仅仅测试了这个。 2.去http://svnweb.openmoko.org/*chec ... _mci.patch?rev=4096 下载SD/MMC patch,其他晚期版本不可,我就栽倒了这其中间。 3.把 s3c_mci.patch copy到linux-2.6.24.3下。执行patch -p1 s3c_mci.patch 呵呵,早年不会用patch,此次也用上了。 4.make menuconfig,选择MMC/SD ,选择言语CP437,ISO 8859-1,最好也同时选择SCSI相关的工具吧,连USB也一块支撑
[单片机]
S3C2440驱动篇之触摸屏驱动分析
一.硬件简介 S3C2440触摸屏接口与ADC接口集成在一起,触摸屏X、Y坐标所产生的模拟信号通过通道7、5输入,2440提供触摸屏接口有4种处理模式:普通转换模式、分离的X/Y轴坐标转换模式、自动X/Y轴坐标转换模式、等待中断模式。具体参考2440硬件手册16章。 二.驱动实现 下面是触摸屏驱动源码,其中使用了linux输入子系统input。暂时还没研究这一块,想深入了解可参考相关资料。 #include linux/errno.h #include linux/kernel.h #include linux/module.h #include linux/slab.h #include linux
[单片机]
s3c2440裸机-I2c编程-3.i2c中断服务程序
Start信号之后,发出设备地址,在第9个时钟就会产生一个中断,我们根据i2c的流程图来编写中断程序。 每传输完一个数据将产生一个中断,I2C操作的主体在中断服务程序,它可以分为两部分:写操作,读操作。 完整code如下: static p_i2c_msg p_cur_msg; int isLastData(void) { if (p_cur_msg- cnt_transferred == p_cur_msg- len - 1) return 1; /* 正要开始传输最后一个数据 */ else return 0; } void resume_iic_with_
[单片机]
s3c2440串口编程
0、串口总线标准:RS-232C RS-422A RS-485 1、串口通讯,分为同步通讯和异步通讯,我们通常使用的都是异步串口。通讯时,双方先约定好数据帧的格式,即波特率,数据位,停止位,奇偶校验位等。 2、串口通信数据格式 3、串口接线 1) 两端的插头上有1-9的标号,如果用万用表测相对应号码的针都是短通的,就是直连线。 不是一对一通的就是交叉。 交叉就是收-发,发-收,一样设备用平行就会发-发,收-收 我们用的是交叉串口线。 2) 进行TTL与EIA电平转换:CPU和终端均采用TTL电平及正逻辑,它们与EIA采用的电平及负逻辑不兼容,需在接口电路中进行转换。 正逻辑:高电平---1
[单片机]
<font color='red'>s3c2440</font>串口编程
S3C2440串口的基本使用
2440A有三个串口,我们使用串口0对它进行了解熟悉。 首先肯定是应该找到手册上串口0所对应的引脚,然后配置相应寄存器。 串口0对应GPIO H的 2,3 串口在单片机中我们已经有很多使用经验了,对于协议采用 8-N-1,8bit数据位,无校验,1停止位。 说明波特率的计算方式: 把串口对应IO配置成 TX和RX功能之后,我们需要对指定寄存器进行读写操作,实现串口的接发。 具体的寄存器就不贴出来了。手册上都有,这里不使用FIFO和中断方式,只是最基本的接发操作。 main.c: #include s3c2440_gpio.h #include s3c2440_soc.h #include uart.h
[单片机]
<font color='red'>S3C2440</font>串口的基本使用
S3C2440 中断控制寄存器
1、 SUBSRCPND 寄存器(SUB SOURCE PENDING) SUBSRCPND 寄存器被用来标识 INT_RXD0、INT_TXD0 等中断(S3C2410中这类中断有 11 个,而 S3C2440 中有 15 个)是否已经发生,每位对应一个中断。当这些中断发生并且没有被 INTSUBMSK 寄存器屏蔽,则它们中的若干位将“汇集”出现在 SRCPND 寄存器的一位上。比如 SUBSRCPND 寄存器中的 3 个中断 INT_RXD0、INT_TXD0、INT_ERR0,只要有一个发生了并且它没有被屏蔽,则 SRCPND 寄存器中的 INT_UART0 位被置 1。 要清楚中断时,往 SUBSRCPND 寄存器中某
[单片机]
<font color='red'>S3C2440</font> <font color='red'>中断</font>控制寄存器
S3C2440使用RAM来挂载jffs2文件系统
开发环境: PC主机: Ubuntu 和 RedHat 9.0 目标板:TQ2440开发板,linux内核2.6.30,根文件系统为YAFFS。 0. 引言 在yaffs2,jffs2和cramfs这3个文件系统中,只有cramfs可以作为loop device使用mount命令来挂载,而其他两个则不行。 不过内核可以使用RAM来模拟一个块设备,从而可以让jffs2文件系统使用mount命令来挂载文件系统。 至于yaffs2文件系统的挂载,目前还不知道有什么方法。 本文将对如何使用RAM来挂载jffs2文件系统做简要说明。 1. 配置开发板Linux内核 首先,打开test driver using RAM,这里将
[单片机]
<font color='red'>S3C2440</font>使用RAM来挂载jffs2文件系统
s3c2440 内存管理单元MMU学习笔记
学习了S3C2440内存管理单元MMU,主要参考了《嵌入式Linux应用开发完全手册》 (下载见 http://www.linuxidc.com/Linux/2011-01/31114.htm )。有两篇文章也说得很详细,分别是 http://www.linuxidc.com/Linux/2011-09/43526.htm 与 http://www.linuxidc.com/Linux/2011-09/43525.htm 感兴趣的可以参考一下,我就不重复内容了。但是它们在MMU地址变换过程这一块说得不太好理解。在此,我就按照我的理解将《嵌入式Linux应用开发完全手册》的相关内容解释一下,图片加点注释。如有错误,请大家不吝赐教,我
[单片机]
<font color='red'>s3c2440</font> 内存管理单元MMU学习笔记

推荐帖子

智能哑铃
本帖最后由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得捷技术专区
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件
更多每日新闻

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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