新闻中心

EEPW首页>嵌入式系统>设计应用> ARM微处理器的编程模型之:异常中断处理

ARM微处理器的编程模型之:异常中断处理

作者: 时间:2013-09-13 来源:网络 收藏

本文引用地址://m.amcfsurvey.com/article/257085.htm

4.在特权模式下使用SWI异常处理

在特权模式下使用SWI异常处理,和IRQ/FIQ中断嵌套基本类似。当执行SWI指令后,处理器执行下面操作。

① 处理器进入特权模式。

② 将程序状态字内容CPSR保存到SPSR_svc。

③ 返回地址放入LR_svc。

如果处理器已经处于特权模式,再发生SWI异常,则LR_svc和SPSR_svc寄存器的值将丢失。

所以在特权模式下,调用SWI软中断异常,必须先将LR_svc和SPSR_svc寄存器的值压栈保护。下面的例子显示了一个可以在特权模式下调用的SWI处理函数。

AREA SWI_Area, CODE, READONLY

PRESERVE8

EXPORT SWI_Handler

IMPORT C_SWI_Handler

T_bit EQU 0x20

SWI_Handler

STMFD sp!,{r0-r3,r12,lr} ;寄存器压栈保护

MOV r1, sp ;堆栈指针放r1作为参数传递.

MRS r0, spsr ;读取spsr.

STMFD sp!, {r0, r3} ;将spsr压栈保护

;

;

LDR r0,[lr,#-4] ;计算SWI指令地址.

BIC r0,r0,#0xFF000000 ;读取SWI中断向量号.

; r0存放中断向量号

; r1 堆栈指针

BL C_SWI_Handler ;调用C程序的SWI处理函数.

LDMFD sp!, {r0, r3} ;从堆栈中读取spsr.

MSR spsr_cf, r0 ;恢复spcr

LDMFD sp!, {r0-r3,r12,pc}^ ;恢复其他寄存器并返回.

END

5.从应用程序中调用SWI

可从汇编语言或 C/C++ 中调用 SWI。

(1)从汇编应用程序中调用SWI

从汇编语言程序中调用SWI,只要遵循AAPCS标准即可。调用前,设定所有必须的值并发出相关的 SWI。例如:

MOV r0, #65 ; 将软中断的子功能号放到r0中

SWI 0x0

注意

SWI指令和其他所以指令一样,可以被条件执行。

(2)从C应用程序中调用SWI

在C或C++应用程序中调用SWI,要将C语言的子程序用编译器扩展_swi声明,例如:

__swi(0) void my_swi(int);

……

……

……

my_swi(65);

编译器扩展_swi确保了SWI以内联方式进行编译,而没有额外的开销。但有如下的AAPCS限制。

· 函数调用参数只能使用r0~r3传递。

· 函数返回值只能通过r0~r3传递。

向内联的SWI函数传递参数和向实际的子函数传递参数基本类似。但返回值的情况比较复杂。如果有两到四个返回值,则必须告诉编译程序返回值是以结构形式返回的,并使用__value_in_regs 伪操作声明。这是因为基于结构值的函数通常被处理为一个void(空)型函数,且第一个自变量必须为存放结果结构的地址。

下面的例子显示了对编号为0x0、0x1、0x2和0x3的SWI软中断的调用。其中,SWI0x0和SWI0x1传递两个整型参数并返回一个单一结果;SWI0x2传递4个参数并返回一个单一结果;而SWI0x3传递4个参数并通过结构体返回4个结果。

#include stdio.h>

#include swi.h

unsigned *swi_vec = (unsigned *)0x08;

extern void SWI_Handler(void);

int main( void )

{

int result1, result2;

struct four_results res_3;

Install_Handler( (unsigned) SWI_Handler, swi_vec );

printf(result1 = multiply_two(2,4) = %dn, result1 = multiply_two(2,4));

printf(result2 = multiply_two(3,6) = %dn, result2 = multiply_two(3,6));

printf(add_two( result1, result2 ) = %dn, add_two( result1, result2 ));

printf(add_multiply_two(2,4,3,6) = %dn, add_multiply_two(2,4,3,6));

res_3 = many_operations( 12, 4, 3, 1 );

printf(res_3.a = %dn, res_3.a );

printf(res_3.b = %dn, res_3.b );

printf(res_3.c = %dn, res_3.c );

printf(res_3.d = %dn, res_3.d );

return 0;

}

__swi(0) int multiply_two(int, int);

__swi(1) int add_two(int, int);

__swi(2) int add_multiply_two(int, int, int, int);

struct four_results

{

int a;

int b;

int c;

int d;

};

__swi(3) __value_in_regs struct four_results many_operations(int, int, int, int);

(3)应用程序中动态调用SWI

在某些情形下,需要调用直到运行时才会知道其编号的 SWI。例如,当有很多相关操作可在同一目标上执行,并且每一个操作都有其自己的 SWI 时,就会发生这种情况。在此情况下,上一小节的方法不适用。

解决的方法有两种。

· 在运行时得到SWI功能号,然后构造出相应的SWI指令的编码,将该编码保存在某个存储单元中,将PC指针指向该单元,执行指令。

· 使用一个通用的SWI程序,将运行时需要调用的SWI功能号作为参数传递给该通用的SWI异常处理程序,通用的SWI程序根据参数值调用相应的SWI处理程序完成需要的操作。

通过汇编语言可以实现第二种解决办法:通过寄存器(通常为r0或r12)传递所需要的操作数,这样可以重新编写SWI处理程序,对相应寄存器中的值进行处理。

但有些情况下,为了节省程序开销,需要直接使用SWI中断号对程序调用。例如,操作系统可能会使用单一的一条SWI指令并用寄存器来传递所需运算的编号。这使得其他SWI空间可用于特定应用程序的SWI。在一个特定的应用程序中,如果从指令中提取SWI编号的开销太大,就可使用这个方法。(0x123456)和Thumb(0xAB)半主机方式的SWI就是这样实现的。



评论


相关推荐

技术专区

关闭