ARM异常处理和软中断使用

1 ARM中断服务程序的函数名是固定的?

  • 印象中学C51时,只需要使能中断,然后编写任意名词的中断函数就可以了。但是编写中断函数时,会写明中断号。现在想来,应该是编译器根据中断号,修改了中断向量表中对应中断的跳转地址。一般中断向量表中存放的是中断服务程序的地址。
  • 对于stm32,也就是cortex-M系列,中断函数有固定的函数名。因为启动文件中,中断向量存放的是可执行代码,其中写明了要跳转到的中断处理程序的名称。中断向量表是属于code区的。
  • arm9比cortex要老,arm9同样也是有固定的中断函数名。
    看一下keil提供的s3c2440的启动文件。
    中断向量表:
Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr

这些xxxx_Addr处是什么?

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                   ; Reserved Address 

可以看到,每个xxxx_addr处用DCD伪指令给固定名字的一段程序分配了连续空间。
那比如Reset_Handler这段程序做了什么?在启动文件中找到对应的代码,可以发现Reset_Handler是一大段汇编程序,做了很多事,比如初始化watchdog,初始化时钟,初始化存储控制器等等,可以发现,启动文件中所有的可执行程序(内存分配这种伪指令是汇编器做的,对arm来说不算code),除了最开始的几句是中断向量表中的指令,其余都是在Reset_Handler下的。

  • 其实启动文件真的非常简单,程序装入RAM后,按顺序执行AREA RESET, CODE, READONLY下的汇编程序,即会先执行Reset_Handler,其中会做一些初始化,然后跳转到c程序。而LDR PC, Reset_Addr之后的汇编程序是执行不到了,它们会在对于的中断触发时执行。

2 异常处理

  • arm的exception vectors一般从0x0开始,也可以从0xFFFF0000开始。这时候0x0是需要做一些别的事。
    下图是Reset中断触发后执行过程。
    ARM异常处理和软中断使用_第1张图片
  • 异常(Exception)和中断(Interrupt)的区别是什么?
    看一下 armv5的Arm Architecture Reference Manual 中的描述,Exception包括了fast interrupt,normal interrupt,software interrupt等中断,此外还有memory aborts、undefined instruction,一共五种类型的异常。
    看一下启动文件,可以发现这几种异常的执行机制都是一样的,只不过触发方式不同,触发后执行的程序不同。
    所以我认为Exception和Interrupt是一样的,可能arm中interrupt更多样所以叫Exception。

ARM异常处理和软中断使用_第2张图片

  • 安装handler
    下面这张图说了操作系统在运行前会为每个异常安装handler,也就是中断服务程序。
    ARM异常处理和软中断使用_第3张图片
    对于裸机也是一样,查看keil中的s3c2440文件,可以发现这些中断都是一直跳转到自身,进行死循环。
    ARM异常处理和软中断使用_第4张图片

3 软中断的安装

  • 软中断在裸机上似乎不是那么重要,但是在操作系统上是必须的。对Linux来说,软中断是用户空间进入内核空间的唯一接口,我们常用的printffprintf()等函数的源码中就包含了系统调用(system call),而系统调用中就触发了软中断,然后将应用程序中的一些信息打印到屏幕,或写到硬盘。调用软中断相对应用程序的运行来说是比较费时间的,所以在编写Linux应用程序时要尽可能少的减少系统调用。
  • 下面是一个s3c2440裸机安装软中断的实验,软中断的中断号为0时,实现64位加法,中断号为1时,实现两个32位数乘法。
    通过keil自带的simulator进行仿真。这里安装指的是在程序里通过一段代码来设置软中断的异常处理函数,而不是在启动文件里写一段固定的代码用于软中断的异常处理。
  • 工程链接

3.1 c语言和汇编语言的参数传递

  • c和汇编语言的参数传递会用到。

  • c语言和汇编语言参数传递可以根据参数的数量分成两种情况:

    1. 参数小于等于四个时,会使用r0-r4进行按照顺序进行参数传递。传递更多参数的一种方法是将这多个参数保存到一个指针,然后将指针做为参数,这样寄存器保存的就会是地址。
    2. 大于四个时,第5个及之后的参数会保存到栈里。
  • 参数传递和返回结果,编写汇编程序时具体有两种方式:
    一是需要把传递过来的r0-r3先入栈,然后通过sp来寻址,将r0-r3中需要的数据传递到寄存器,因为无法直接对内存中的数据进行计算。使用寄存器计算完成后,再通过sp来寻址,将结果写入内存。最后再出栈,此时r0-r3就是要返回的结果了。 看起来整个过程有点混乱,但写汇编时自然就会用到了。尤其是以上描述中,有时汇编语言会将sp放入r0,然后传给c函数,在c函数中就完成了内存中数据的修改。写汇编语言一定要考虑清楚栈的操作,即对寄存器sp(r13)的操作
    二是直接操作r0-r3然后进行返回,只能用于参数小于等于四个情况。注意此时不要不要将r0-r3先入栈,最后再出栈,否则操作后的r0-r3就被覆盖掉了。

3.2 arm工作模式

arm有7个工作模式,每个工作模式有不同的寄存器。用keil调试时,直接可以在register界面看到每个模式下寄存器的值,keil也会把当前模式的名称给加粗。如下图是运行在user/system 模式。
ARM异常处理和软中断使用_第5张图片
栈只有一个的,但可以看到不同模式下有不同的sp,启动文件里也设置了不同模式下栈的大小。模式切换时,sp寄存器就会切换到对应模式。不同模式虽然sp不同,但是整个内存是没有任何隔离的,很可能会出现的一种情况是,一种模式下入栈,导致另一种模式的栈被覆盖! 因此需要确保每种模式的栈设置的足够大。 但也不能过大,浪费内存。

swi属于supervisor mode。当swi触发时,处理器就进入supervisor mode,开始使用当前模式下的寄存器。之后开始执行中断服务程序,注意,中断服务程序执行完后,模式是不会自动切换的,需要手动恢复cpsr。
比如 ldmfd sp!, {r0-r12,lr}^ ,加上^后,cpsr就会被spsr覆盖,模式就得以切回。或者也可以手动对cpsr赋值。

你可能感兴趣的:(arm裸机)