s3c2440裸机-异常中断2-und未定义指令异常

发布者:平安宁静最新更新时间:2024-07-05 来源: elecfans关键字:异常中断  und 手机看文章 扫描二维码
随时随地手机看文章

1._und(未定义指令异常)介绍

我们之前分析过5种异常,那么如何进入未定义指令异常,当然是cpu读取指令发生异常,出现了指令解析异常。 我们先来看下当cpu解析到什么样的指令才会触发未定义指令异常呢?


从上面的arm指令格式中可知,只要指令码属于划线的格式,就属于未定义指令异常。


2.汇编向c函数传参

我们知道汇编给C语言函数传参是通过r0,r1,...通过堆栈的方式去传递的参数,比如r0=1, r1=2;那么在被调用的c函数中argv0就是r0, argv1就是r1...,那么我们如果通过汇编给C函数传递字符串呢?


我们可以通过这样声明und_string为一个字符串:


und_string:

    .string 'undefined instruction exception'

然后用ldr r1, =und_string,这样r1中就保存了und_string的地址。 这样调用我们的c函数就可以把und_string传入进去。


3._und异常程序示例

我们现在定义一条未定义指令伪代码:


.text

.global _start


_start:

    b reset  /* vector 0 : reset */ 

    b do_und /* vector 4 : und (看中断向量表)*/


reset:

    /*看门狗

    时钟

    sdram

    设置SP

    重定位*/

    ...

    bl print1


und_code:

    .word 0xdeadc0de; /*定义一条未定义指令*/

    /*故意以一个数据的方式引入一条未定义指令,当cpu执行到这里,读取0xdeadc0de指令码的时候,

    发现无法识别这条指令,就发生未定义指令异常,就跳转到0x4的中断向量去执行*/


    bl print2

    ...

我们现在为了方便调试理解:我们在未定义指令异常前后加上打印print1, print2,如果出现未定义指令异常后,就会跳到0x4的地方去读取指令,print2也就没法执行。


当跳转到0x4的中断向量后,发现此处是一条跳转指令'bl do_und', 我们再到未定义指令异常的服务程序do_und中打印出und_string这个字符串的内容。


现在开始写指令异常的服务程序do_und,实现如下:


do_und:

    /* sp_und未设置, 先设置它 (由于之前一直处于管理模式,现在处在und状态)*/

    ldr sp, =0x34000000


    /* 保存现场 */

    /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */

    /* lr是异常处理完后的返回地址, 也要保存 */

    stmdb sp!, {r0-r12, lr}  /*先减后存*/ /* 把栈中的值备份到r0-r12*/


    /* 处理und异常 */

    mrs r0, cpsr

    ldr r1, =und_string /*保存und_string地址*/

    bl printException


    /* 恢复现场 */

    ldmia sp!, {r0-r12, pc}^  /*(ldmia先读后加),把备份的值恢复到栈中,让pc=lr就可以恢复到异常前的指令地址。^会把spsr的值恢复到cpsr里 */

下面来分析一下这个未定义指令异常服务程序:(其实代码的注释已经讲的很详细了)


1.进入未定义指令异常服务do_und之前硬件自动完成的事情如下:


1. lr_und保存有被中断模式中的下一条即将执行的指令的地址

     2. SPSR_und保存有被中断模式的CPSR

     3. CPSR中的M4-M0被设置为11011, 进入到und模式

     4. 跳到0x4的地方执行程序 (bl do_und)

2.进入指令异常服务程序do_und后,我们需要保存现场,处理und异常,恢复现场,注意:由于发生了cpu模式切换,如果要用到栈,那么先要设置对应模式的栈。由于栈的地址是向下生长的,这里我就用sdram的末位地址作为栈指针,把sp_und=0x34000000。


3.在und异常服务程序中有可能会用到栈, 所以先保存现场,通过stmdb sp!, {r0-r12, lr}语句把栈中的值备份到r0-r12和lr,然后恢复现场的时候通过ldmia sp!, {r0-r12, pc}^,详见上面的注释。


4.我们看到保存现场后,我们把cpsr的值放到r0, 把und_string放到r1, 然后用bl printException调用c函数,这样我们的c函数printException就能收到汇编传过来的参数,一个是cpsr模式(r0),一个是und_string汇编传过来的字符串(r1)。我们用C函数实现printException:


void printException(unsigned int cpsr, char *str)

{

    puts('Exception! cpsr = ');

    printHex(cpsr);

    puts(' ');

    puts(str);

    puts('nr');

}

完整的代码如下:




点击展开代码


.text

    .global _start



_start:

    b reset  /* vector 0 : reset */


    b do_und /* vector 4 : und (看中断向量表)*/


do_und:

    /* 执行到这里之前:

     * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址

     * 2. SPSR_und保存有被中断模式的CPSR

     * 3. CPSR中的M4-M0被设置为11011, 进入到und模式

     * 4. 跳到0x4的地方执行程序 (bl do_und)

     */



    /* sp_und未设置, 先设置它 (由于之前一直处于管理模式,现在处在und状态)*/

    ldr sp, =0x34000000


    /* 保存现场 */

    /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */

    /* lr是异常处理完后的返回地址, 也要保存 */

    stmdb sp!, {r0-r12, lr}  /*先减后存*/ /* 把栈中的值备份到r0-r12*/


    /* 处理und异常 */

    mrs r0, cpsr

    ldr r1, =und_string /*保存und_string地址*/

    bl printException


    /* 恢复现场 */

    ldmia sp!, {r0-r12, pc}^  /*(ldmia先读后加),把备份的值恢复到栈中,让pc=lr就可以恢复到异常前的指令地址。^会把spsr的值恢复到cpsr里 */


und_string:

    .string 'undefined instruction exception'



reset:

    /* 关闭看门狗 */

    /* 时钟 */

    /* sdram */ 

    bl copy2sdram

    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

测试结果如下:


打印出print1中的字符串‘abc’后,紧接着打印printException函数中的结果,cpsr=0x600000db,那么对应的M[4:0]=11011, 对应下图为und模式。然后从und异常返回,恢复原来的模式继续执行。


4.上述代码改进:

1.保证指令4字节对齐

我们将上面的代码的und_string字符串修改一下:


...

und_string:

    .string 'undef instruction'


reset:

    /* 关闭看门狗 */

    /* 时钟 */

...

编译烧录再次运行,发现没有任何打印输出,这是为什么呢?我明明只是把und_string字符串改了一下呀。


查看反汇编


我们发现reset的地址是0x30000032,竟然不是4字节对齐的,我们知道arm指令集是以4字节为基本单位的,那么这里没有对齐,肯定无法解析指令。那么我们手工改进代码如下:


...

und_string:

    .string 'undef instruction'

.align 4


reset:

    /* 关闭看门狗 */

    /* 时钟 */

...



我们再来看看反汇编,发现reset的地址是30000040,是以4字节对齐的,再次烧录运行,发现能够正常输出print1, 能够进入未定义指令异常。


2.中断向量表进入异常向量用绝对跳转

如果我们程序非常大,中断向量入口代码的地址可能会大于sram的容量4k,比如do_und和do_swi,那么这个时候就需要用绝对跳转。


.text

.global _start

_start:

    b reset  /* vector 0 : reset */ 

    b do_und /* vector 4 : und (看中断向量表)*/

将上面的相对跳转换成如下代码:


.text

.global _start


_start:

    b reset  

    ldr pc, und_addr 

    ldr pc, swi_addr

    ...

    ... 

und_addr:

    .word do_und

swi_addr:

    .word do_swi

这样我们的do_und, do_swi就可放在4k之外的地方。


3.重定位完程序后马上跳转到sdram上执行

我们现在不断增加的程序代码量,那么有可能在 'ldr pc, =main' 这条指令执行之前程序就已经超过4k。那么我们当从nand启动的时候,还没执行到ldr pc, =main这句来,就无法取指令执行了。 所以我们干脆重定位完代码后就直接跳转到sdram上去执行,代码裁剪如下:


...

    reset:

        /*

        看门狗

        时钟

        set SP

        sdram_init

        重定位

                    */

ldr pc, =sdram sdram:


...

    ldr pc, =main  /* 绝对跳转, 跳到SDRAM */


    halt:

        b halt

我们再来分析下整个程序执行过程:



1.一上电,cpu从0地址执行,执行b reset(进行初始化硬件)

2.重定位程序

3.跳转到sdram去继续执行

4.执行到 deadc0de,发生未定义指令异常

5.跳转到异常向量表的0x4地址去执行

6.跳转到sdram上执行异常处理函数(do_und)

7.异常返回,继续执行


关键字:异常中断  und 引用地址:s3c2440裸机-异常中断2-und未定义指令异常

上一篇:s3c2440裸机-异常中断3-swi软中断
下一篇:s3c2440裸机-异常中断1-异常中断的原理与流程

推荐阅读最新更新时间:2026-03-22 13:55

s3c2440裸机-异常中断3-swi软中断
swi(软中断) 我们知道arm有7中工作模式,除了usr模式,其他6种都是特权模式。我们知道usr模式无法修改CPSR直接进入其他特权模式,但linux应用程序一般运行在usr模式,既然usr模式权限非常低,是无法直接访问硬件寄存器的,那么它是如何访问硬件的呢? linux应用程序是通过系统调用,从而进入内核态,运行驱动程序来访问的硬件,那么系统调用又是如何实现的呢,就是通过软中断swi指令来进入svc模式,进入到svc模式后当然就能访问硬件啦。 所以我们的应用程序在usr模式想访问硬件,必须切换模式,怎么切换? 有以下两种方式: 1.发生异常或中断(被动的) 2.swi + 某个值(主动的) 现在介绍如何进
[单片机]
s3c2440裸机-异常中断4-irq外部中断
我们回顾下中断产生前后的处理流程:详见异常、中断的原理与流程 中断前: 中断产生后: 问题案例: 我们想实现一个按键点灯程序,我们知道有以下两种方案: 1.轮询方案:轮询检测按键的电平状态,当检测到被按下后,对应的gpio会拉低,点亮对应的led;(略) 2.中断方案:将按键配置成外部中断源,当有按键按下,触发中断,在中断服务程序(isr)中去完成点灯。下面开始写代码: 一.中断初始化 1)中断源设置 我们用按键作为外部中断源,我们把按键对应的gpio配置成中断引脚,当按键按下,相应的gpio产生了电平跳变,就会触发外部中断。 我们想达到按下按键灯亮,松开按键灯灭这种效果(配成双边沿触发,按下的时候产生下降沿
[单片机]
ARM未定义指令异常学习中关于字节对齐的心得
今天学习jz2440开发板的未定义指令异常这一节课程,跟随韦东山老师编写代码编译烧写后程序老师无法正常运行,经过仔细排查发现是字节未4字节对齐导致的,之前的程序中start.S文件如下开始: _start: /* 关闭看门狗 */ ldr r0, =0x53000000 ldr r1, =0 str r1, 指令从开始就是按照4字节对齐排放的,这样运行程序没有问题。但当在_start标签后增加未定义指令异常处理语句后,代码如下: _start: b reset b do_und do_und: ldr sp, =0x34000000 stmdb sp!, {r0
[单片机]
【ARM裸板】未定义指令异常分析及示例
1.未定义指令异常示例 根据5.1可知,执行异常处理函数之前,硬件会处理的事情: 1.lr_und保存有被中断模式中的下一条即将执行的指令的地址 2.SPSR_und保存被中断模式CPSR 3.CPSR的 = ,进入到und模式 4.跳到0x04的模式执行程序,即跳到b do_und这一指令 /*====================================异常向量表===========================================*/ _start: b reset //vector 0: reset(0地址对应reset) ldr pc, =und_addr //绝对跳转,跳转至s
[单片机]
【ARM裸板】<font color='red'>未定义</font><font color='red'>指令</font><font color='red'>异常</font>分析及示例
S3C2440 裸机程序之音频
/**************************************************************** NAME: u2440mon.c DESC: u2440mon entry point,menu,download HISTORY: Mar.25.2002:purnnamu: S3C2400X profile.c is ported for S3C2410X. Mar.27.2002:purnnamu: DMA is enabled. Apr.01.2002:purnnamu: isDownloadReady flag is added. Apr.10.2002:purnnamu: - Select
[单片机]
s3c2440裸机-UART编程-2-UART编程实现
UART编程 1.初始化 我们的2440支持3个UART串口,以uart0为例讲解。 那么我们需要实现以下这几个函数完成串口的最基本功能: (1)uart0_init()用于初始化串口 (2)putchar()用于发送一个字符 (3)getchar()用于接收一个字符 (4)puts()用于发送一串字符 1.uart0_init() 1.配置uart0引脚 (1)根据原理图GPH2,3用于TxD0, RxD0。 (2)查看dataset,配置GPH控制寄存器,让GPH2,3配成uart模式;为了将其保持为高电平,先设置其为上拉。 GPHCON &= ~((3 4) | (3 6)); G
[单片机]
s3c2440裸机-内存控制器2-不同位宽外设与CPU地址总线的连接
不同位宽设备的连接 我们先看一下2440芯片手册上外设rom是如何与CPU地址总线连接的。 8bit rom与CPU地址线的连接 8bit*2 rom与CPU地址线的连接 8bit*4 rom与CPU地址线的连接 16bit rom与CPU地址线的连接 16bit*2 rom与CPU地址线的连接 从上面的图中,我们知道可以对2片位宽为8bit的外设扩展级联成1个16bit的外设,同理可用4片位宽为8bit的外设进行级联成1个32bit的外设... 从上面的图中,我们还看见一个规律: 当外设总线位宽为8bit时, 外设A0接CPU的地址总线ADDR , A - ADDR ...A - AD
[单片机]
s3c2440裸机-代码重定位-2-编程实现
代码重定位(2.编程实现代码重定位) 1.引入链接脚本 我们上一节讲述了为什么要重定位代码,那么怎么去重定位代码呢? 上一节我们发现 arm-linux-ld -Ttext 0 -Tdata 0x30000000 这种方式编译出来的bin文件有800多M,这肯定是不行的,那么需要怎么把.data段重定位到sdram呢? 可以通过AT参数指定.data段在编译时的存放位置,我们发现这样指定太不方便了,而且不好确定要放在bin文件的哪个位置。这里就要引入链接脚本,它可以帮我们解决这个不必要的麻烦。 链接脚本格式 格式如下图: 我们来看一个具体的例子: SECTIONS { . = 0x00000000; //表示当前
[单片机]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

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