原创 SWI指令--软件中断实例详解 执行过程

2011-4-6 15:20 7564 5 6 分类: MCU/ 嵌入式

 SWI指令---软件中断实例详解

关键字:  SWI,指令,软件中断,实例,详解

SWI,即software interrupt软件中断。该指令产生一个SWI异常。意思就是处理器模式改变为超级用户模式,CPSR寄存器保存到超级用户模式下的SPSR寄存器,并且跳转到SWI向量。其ARM指令格式如下:

SWI{cond} immed_24

Cond域:是可选的条件码 (参见 ARM汇编指令条件执行详解).

immed_24域:范围从 0 到 224-1 的表达式, (即0-16777215)。用户程序可以使用该常数来进入不同的处理流程。

一、方法1:获取immed_24操作数。

为了能实现根据指令中immed_24操作数的不同,跳转到不同的处理程序,所以我们往往需要在SWI异常处理子程序中去获得immed_24操作数的实际内容。获得该操作数内容的方法是在异常处理函数中使用下面指令:

LDR     R0,[LR,#-4]

该指令将链接寄存器LR的内容减去4后所获得的值作为一个地址,然后把该地址的内容装载进R0。此时再使用下面指令,immed_24操作数的内容就保存到了R0:

BIC     R0,R0,#0xFF000000

该指令将R0的高8位清零,并把结果保存到R0,意思就是取R0的低24位。

可能还是有人会问:为什么在SWI异常处理子程序中执行这两条指令后,immed_24操作数的内容就保存到了R0寄存器呢?之所以会有这样的疑问,基本都是因为对LR寄存器的作用没了解清楚。下面介绍一下链接寄存器LR(R14)的作用。

寄存器R14(LR寄存器)有两种特殊功能:

·在任何一种处理器模式下,该模式对应的R14寄存器用来保存子程序的返回地址。当执行BL或BLX指令进行子程序调用时,子程序的返回地址被放置在R14中。这样,只要把R14内容拷贝到PC中,就实现了子程序的返回(具体的子程序返回操作,这里不作详细介绍)。

·当某异常发生时,相应异常模式下的R14被设置成异常返回的地址(对于某些异常,可能是一个偏移量,一个较小的常量)。异常返回类似于子程序返回,但有小小的不同(这里不作详细介绍)。

所谓的子程序的返回地址,实际就是调用指令的下一条指令的地址,也就是BL或BLX指令的下一条指令的地址。所谓的异常的返回的地址,就是异常发生前,CPU执行的最后一条指令的下一条指令的地址。

例如:(子程序返回地址示例)

指令                       指令所在地址

ADD     R2,R1,R3             ;0x300000

BL      subC                   ;0x300004

MOV     R1,#2                  ;0x300008

BL指令执行后,R14中保存的子程序subC的返回地址是0x300008。

再例如:(异常返回地址示例)

指令                       指令所在地址

ADD     R2,R1,R3             ;0x300000

SWI     0x98                   ;0x300004

MOV     R1,#2                  ;0x300008

SWI指令执行后,进入SWI异常处理程序,此时R14中保存的返回地址为0x300008。

所以,在SWI异常处理子程序中执行 LDR  R0,[LR,#-4]语句,实际就是把产生本次SWI异常的SWI指令的内容(如:SWI   0x98)装进R0寄存器。又因为SWI指令的低24位保存了指令的操作数(如:0x98),所以再执行BIC   R0,R0,#0xFF000000语句,就可以获得immed_24操作数的实际内容。

二、方法2:使用参数寄存器。

    实际上,在SWI异常处理子程序的实现时,还可以绕开immed_24操作数的获取操作,这就是说,我们可以不去获取immed_24操作数的实际内容,也能实现SWI异常的分支处理。这就需要使用R0-R4寄存器,其中R0-R4可任意选择其中一个,一般选择R0,遵从ATPCS原则。

    具体方法就是,在执行SWI指令之前,给R0赋予某个数值,然后在SWI异常处理子程序中根据R0值实现不同的分支处理。例如:

指令                       指令所在地址

MOV     R0,#1                  ; #1给R0

SWI     0x98                   ; 产生SWI中断,执行异常处理程序SoftwareInterrupt

ADD     R2,R1,R3             ;

 

;SWI异常处理子程序如下

SoftwareInterrupt

        CMP     R0, #6              ; if R0 < 6

        LDRLO   PC, [PC, R0, LSL #2] ; if R0 < 6,PC = PC + R0*4,else next

        MOVS    PC, LR

SwiFunction

        DCD     function0     ;0

        DCD     function1     ;1

        DCD     function2     ;2

        DCD     function3     ;3

        DCD     function4     ;4

        DCD     function5     ;5

Function0

    异常处理分支0代码

Function1

    异常处理分支1代码

function2

    异常处理分支2代码

function3

    异常处理分支3代码

function4

    异常处理分支4代码

function5

    异常处理分支5代码

    在ARM体系结构中,当正确读取了PC的值时,该值为当前指令地址值加8字节,也就是说,对于ARM指令集来说,读出的PC值指向当前指令的下两条指令的地址,本例中就是指向SwiFunction 表头DCD  function0 这个地址,在该地址中保存了异常处理子分支function0的入口地址。所以,当进入SWI异常处理子程序SoftwareInterrupt时,如果R0=0,执行LDRLO   PC, [PC, R0, LSL #2]语句后,PC的内容即为function0的入口地址,即程序跳转到了function0执行。在本例中,因为R0=1,所以,实际程序是跳转到了function1执行。R0左移2位(LDRLO   PC, [PC, R0, LSL #2]),即R0*4,是因为ARM指令是字(4个字节)对齐的DCD  function0等伪指令也是按4字节对齐的。

    在本方法的实现中,实际指令中的24位立即数(immed_24域)被忽略了, 就是说immed_24域可以为任意合法的值。如在本例中,不一定使用SWI 0x98,还可以为SWI   0x00或者SWI 0x01等等,程序还是会进入SWI异常处理子程序SoftwareInterrupt,然后根据R0

的内容跳转到相应的子分支。

三、KEIL CARM编译器中SWI的使用方法:

    待续

四、ADS中C语言编程SWI的使用方法:

        待续

五、执行过程:

 我下拉周立功的ARM2100模版 然后在target.h中加了
  __swi(0x00) void SwiHandle1(int Handle);
 #define IRQDisable() SwiHandle1(0)
 #define IRQEnable() SwiHandle1(1)
 #define FIQDisable() SwiHandle1(2)
 #define FIQEnable() SwiHandle1(3)
 在startup.s中
;软中断
SoftwareInterrupt
        B       SoftwareInterrupt (从这里开始改)
        CMP R0,#4
        LDRLO PC,[PC,R0,LSL#2]
        MOVS PC,LR
SwiFunction 
        DCD  IRQDisable
        DCD  IRQEnable
        DCD  FIQDisable
        DCD  FIQEnable
 IRQDisable
        MRS R0,SPSR
        ORR R0,R0,#NoInt
        MSR SPSR_c,R0
        MOVS PC,LR
 IRQEnable
        MRS R0,SPSR
        BIC R0,R0,#NoInt
        MSR SPSR_c,R0
        MOVS PC,LR
 FIQDisable
        MRS R0,SPSR
        ORR R0,R0,#NoFIQ
        MSR SPSR_c,R0
        MOVS PC,LR
FIQEnable
        MRS R0,SPSR
        BIC R0,R0,#NoFIQ
        MSR SPSR_c,R0
        MOVS PC,LR    (改写到这里结束)
;取指令中止
PrefetchAbort
        B       PrefetchAbort
其余没变
然后在main.c中用了IRQEnable();
我想请问下程序到了IRQEnable();这一步然后调用函数 怎么就使能IRQ了呢  实在不懂  不懂他的执行过程 ?

答:
__swi是ADS编译器的关键字,用它做前缀可以声明一个软中断调用,格式为:
__swi(功能号)   返回值  名称 (参数列表)
功能号:即软中断指令中的24位立即数,软中断号
名  称:即调用软中断时用于描述软中断的函数名称
参  数:软中断函数的参数,根据ATPCS规则,如果软中断函数有不超过4个参数时,通过R0~R3传递,超过4个参数时用堆栈来传递。
—swi(0x00) void SwiHandle1(int Handle)。其中0x00为软中断功能号(软中断号);软中断函数名称为SwiHandle1;只有一个参数,则使用R0来传递;函数没有返回值。
紧接着这句代码的是定义了4个宏,分别表示禁能IRQ函数、使能IRQ函数、禁能FIQ函数、使能IFQ函数,其实调用的软中断函数是一样的,只是参数不同而已。例如在用户程序中调用“IRQEnable( );”时,处理器会产生软中断。
位于启动代码中的那些是软中断处理函数,当发生软中断时,PC被强制指向0x00000008,这个地址中存放的是软中断异常的处理函数的地址,所以程序会跳转至标号“SoftwareInterrupt ”处执行。SoftwareInterrupt 函数的功能是判断R0的值(R0的值为软中断函数传递过来的参数)是否小于4,如果小于4则跳转至标号“SwiFunction”执行,如果不是则函数返回。SwiFunction函数是一个散转函数,它的功能是根据R0的值跳转至对应的函数处执行,即如果参数为1,则函数会跳转至IRQEnable处执行,将IRQ中断使能。
你的代码好像有问题,B SoftwareInterrupt 会死循环了

PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户377235 2014-7-9 20:43

学习了,谢了
相关推荐阅读
cz81_503897891 2011-11-21 17:13
博客备份--查找
大家好:     各位朋友现在博客如何备份?如何指定关键词进行查找?     以前的版本可以,现在新版以来我一直找这个功能,找不到. 谢谢...
cz81_503897891 2011-11-01 11:20
查看Keil MDK-ARM各种数据类型占用的字节数
  笔者正在学习uCOS-II,移植到ARM时考虑到数据类型的定义,但对于Keil MDK编译器的数据类型定义还是很模糊,主要就是区分不了short int、i...
cz81_503897891 2011-10-28 16:44
理解指针函数 区别:*(pfun(int, int))与 (*pfun)(int, int)
1.int *(pfun(int, int)):一个返回值为整型指针的函数 2.int (*pfun)(int, int):是一个指向返回值为int的函数的指针 3.#define  IAP_ENT...
cz81_503897891 2011-10-20 15:49
各种接口与管脚 232,VGA、USB、DVI、PS/2 RJ45网线、HDMI 的接口引脚定义
232,VGA、USB、DVI、PS/2 RJ45网线、HDMI 的接口引脚定义 学习 2010-06-07 20:20:15 阅读82 评论0 字号:大中小 经常看到网络上很多人要各种电脑接口引...
cz81_503897891 2011-10-20 11:50
ARM 中断编程入门 寄存器介绍及编写过程
最近一直在和ARM2200打交道,总结了下在ARM zlg模版中如和编写中断的方法(涉及了向量中断,非向量中断和外部中断的编写方法) 本人初学,以下为本人观点,若有错误,请与我联系交流 一.   ...
cz81_503897891 2011-10-18 17:17
LPC2478 向量中断控制器(VIC)
简介:         VIC是处于芯片外设和ARM内核之间的一个模块,对芯片所有外设中断进行管理,决定哪些中断源可以产生中断、产生哪种类型的中断以及中断后执行哪段服务程序。  ARM处理器内核具...
EE直播间
更多
我要评论
1
5
关闭 站长推荐上一条 /3 下一条