ARM中断和异常


目录:
中断与异常
定义
外部中断外部中断
软件中断自陷
异常
异常向量表
具体内容
ARM 引起的异常事件类型
异常响应过程
Reset 异常
未定义指令异常
软件中断异常
指令预取异常
数据中止异常
IRQ
FIQ
异常返回
异常优先级
异常处理中的寄存器使用
中断处理
中断与异常
定义
ARM 中的工作模式除 User 和 System 外,均为异常模式,这里的异常是广义的,包含以下三类情况
外部中断(外部中断)
由于 CPU 外部的原因而改变程序执行流程,属于异步事件,可以屏蔽
软件中断(自陷)
通过处理器拥有的软件指令,可预期地使正在执行的程序改变执行流程,以执行特定的程序
显式的事件,无条件执行
属同步事件,且不可屏蔽
例如 Motorola 68000 系列的 Trap 指令、ARM 中的 SWI、Intel 8086 中的 INT
异常
由 CPU 内部的原因(如非法指令)或外部的原因(如访存错误)引起的事件
没有对应的处理器指令
异常发生时,处理器无条件地挂起当前运行的程序,去执行特定的处理程序
属同步事件,且不可屏蔽
ARM 处理器把上述三种事件以异常模式来处理,通过异常向量来响应。ARM 的每个异常向量中存放的是一条指令(一般是跳转指令), 异常发生时,CPU 自动到指定的向量地址读取该指令并执行。
异常向量表
ARM 的异常向量一般是完成程序跳转的指令
在 ARM720T,ARM9 系列及以后的内核中,异常向量表可放置在 0xFFFF0000 开始的高位地址空间
异常向量表
具体内容
异常向量表内容
如果异常处理程序的起始地址位于 32MB 的范围内,可以直接用分支指令完成跳转
如果异常处理程序的起始地址是一个合法的 ARM 指令立即数(可由0~255内的数循环右移偶数位得到),则可以用数据传输指令将该地址加载到 PC 中
其他情况需采用 LDR 指令完成对 PC 的加载
ARM 引起的异常事件类型
综合
异常响应过程
当一个异常产生,处理器核会
把 CPSR 的内容拷贝到对应模式下的 SPSR_< mode> 寄存器中
设置 CPSR 的相关位(改变到ARM态、改变到异常模式 、关闭中断(如果适合))
把返回地址保存在对应模式下的 LR_< mode> 寄存器中
把 PC 拨到异常向量表地址
从异常返回,异常处理程序需要
从 SPSR_< mode> 恢复 CPSR
把 PC 拨到 LR_< mode> 指向的地址(返回地址加载到 PC)
以上只能在 ARM 状态完成
Reset 异常
Reset 被触发时,CPU 进入 Supervisor 模式并禁止 FIQ 和 IRQ
R14_svc = 不确定的值
SPSR_svc = CPSR
CPSR[4:0] = 0b10011 ;进入管理模式
CPSR[5] = 0 ;在ARM态中执行
CPSR[6] = 1 ;禁止快速中断FIQ
CPSR[7] = 1 ;禁止普通中断IRQ
未定义指令异常
触发该异常,有两种情形
CPU 执行一条协处理器指令,未等到任何协处理器的应答
CPU 执行一条未被定义的指令时
一旦发生该异常,CPU 将完成如下动作
R14_und = 未定义指令的下一条指令地址
SPSR_und = CPSR
CPSR[4:0] = 0b11011 ;进入未定义模式
CPSR[5] = 0 ;在ARM态中执行
CPSR[7] = 1 ;禁止普通中断IRQ
常用的异常返回方式
MOVS PC, R14 ;跳过发生异常的指令
SUBS PC, R14, #4 ;重新执行发生异常的指令
软件中断异常
执行 SWI 指令时触发,主要用于进入 OS 的系统调用
软中断
R14_svc = SWI指令的下一条指令地址
SPSR_svc = CPSR
CPSR[4:0] = 0b10011 ;进入管理模式
CPSR[5] = 0 ;在ARM态中执行
CPSR[7] = 1 ;禁止普通中断IRQ
常用的异常返回方式:MOVS PC, R14
指令预取异常
读取指令时发生读内存错误时被标记为中止,该指令还要执行时触发该异常,若未执行,不触发该异常
即 CPU 试图执行已被标记为无效的指令时,触发该异常
R14_abt = 中止指令的下一条指令地址
SPSR_abt = CPSR
CPSR[4:0] = 0b10111 ;进入中止模式
CPSR[5] = 0 ;在ARM态中执行
CPSR[7] = 1 ;禁止普通中断IRQ
常用的异常返回方式:SUBS PC, R14, #4
数据中止异常
CPU 和存储系统之间进行加载/存储数据操作时,若发生错误则触发该异常,分为两种情况
内部中止异常
由CPU内核自己引起,如 MMU/MPU 错误,意味着需要采取正确的措施并重新执行合适的指令
外部中止异常
由存储系统引起,可能是硬件错误,可能是内存地址不存在
R14_abt = 中止指令的下一条指令地址
SPSR_abt = CPSR
CPSR[4:0] = 0b10111 ;进入中止模式
CPSR[5] = 0 ;在ARM态中执行
CPSR[7] = 1 ;禁止普通中断IRQ
常用的异常返回方式
SUBS PC, R14, #4 ;跳过发生异常的指令
SUBS PC, R14, #8 ;返回到发生异常的指令
IRQ
当外部IRQ输入请求发生并且IRQ中断响应已经被使能,触发该异常
R14_irq = 下一条指令地址
SPSR_irq = CPSR
CPSR[4:0] = 0b10010 ;进入IRQ模式
CPSR[5] = 0 ;在ARM态中执行
CPSR[7] = 1 ;禁止普通中断IRQ
常用的异常返回方式:SUBS PC, R14, #4
FIQ
当外部 FIQ 输入请求发生并且 FIQ 中断响应已经被使能,触发该异常
通常用于快速传输数据
R14_fiq = 下一条指令地址
SPSR_fiq = CPSR
CPSR[4:0] = 0b10001 ;进入FIQ模式
CPSR[5] = 0 ;在ARM态中执行
CPSR[6] = 1 ;禁止快速中断FIQ
CPSR[7] = 1 ;禁止普通中断IRQ
常用的异常返回方式:SUBS PC, R14, #4
异常返回
除复位异常外,其他异常在处理完成后都可以返回异常发生时被打断的程序中继续执行
通常在异常处理程序的最后使用一条数据传输指令来完成
在异常模式下,在指令后加后缀 ’ S ’ ,将 PC 作为目标寄存器,这样不仅将更新 PC 的值,还会将 SPSR 寄存器的内容加载到 CPSR 中
校正返回地址的一大原因是 ARM 处理器的流水线
计算返回地址的注意点
发生异常时 PC 的值是否已经更新
异常返回后被中断的指令是否还需要执行
LR = PC - 4
异常优先级
异常被赋以了优先级,据此决定被响应的顺序
异常优先级
在所有异常的入口,IRQ 中断被屏蔽,只有重新被使能才可触发内核响应(如中断嵌套处理)
在 FIQ 和 Reset 异常的入口,FIQ 中断被屏蔽
异常处理中的寄存器使用
异常发生时伴随的模式切换意味着, 被调用的异常处理程序会访问
它自己的堆栈指针 (SP_)
它自己的链接寄存器 (LR_)
它自己的备份程序状态寄存器 (SPSR_)
如果是 FIQ异常处理,5个其它的通用状态寄存器 (r8_FIQ to r12_FIQ)
其它寄存器和原来模式下的寄存器是相同的
在外部接口上,堆栈指针(SP_) 必须保持8字节对齐
异常处理程序在返回前必须保证其它(被破坏的寄存器) 被重新载入,以恢复到异常发生前的状态
这可以通过将工作(即被破坏)寄存器的内容压栈,而在返回前出栈恢复来完成
应用的初始化代码将执行必要的寄存器初始化工作
中断处理
ARM 可响应两个外部中断请求信号,FIQ 和 IRQ
FIQ 中断比 IRQ 中断的优先级更高
当多个中断发生时,首先处理 FIQ
在响应 FIQ 中断时屏蔽 IRQ 中断(和 FIQ中断),直到 FIQ 中断处理程序完成,IRQ 中断才会被处理
以下设计使得 IFQ 尽快的响应
FIQ 向量在异常向量表的最后,使 FIQ 处理程序可以直接从 FIQ 向量开始,省去跳转的时间
FIQ 模式下有五个额外的寄存器 (R8_FIQ to R12_FIQ),这些寄存器在进入和退出 FIQ 时不需保存和恢复
FIQ 中断在 FIQ 中断处理程序的入口是被屏蔽的,需要重新使能(FIQ 中断源可以有多个,但是为了最好的系统性能应该避免 FIQ 中断的嵌套)
绝大多数基于 ARM 的系统有两个以上的中断源,因此需要一个中断控制器 (通常是内存映射编址))来控制中断信号如何进入 ARM 芯片
多级中断
————————————————
版权声明:本文为CSDN博主「纫秋兰以为佩」的编辑整理,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_27421407/article/details/78789686

ARM中,发生中断时,中断控制器会发信号给CPU,告知发生了某种中断。
中断源有按键、定时器、网络数据等
这些信号都可以发送信号给中断控制器,再由中断控制器发送信号给CPU表明有这些中断产生了(中断属于一种异常)
一些异常的发生也可以中断CPU当前运行的程序。比如:
指令不对,数据访问有问题
reset信号,这些都可以中断CPU。 这些是异常中断
中断的处理重点在于保存现场以及恢复现场。
ARM 架构中的异常(中断)处理过程
1 初始化:
​ a 设置中断源,让它可以产生中断
​ b 设置中断控制器(可以屏蔽某个中断,优先级)
​ c 设置CPU总开关,(使能中断)
2 执行其他程序:正常程序
3 产生中断:按下按键--->中断控制器--->CPU
4 cpu每执行完一条指令都会检查有无中断/异常产生
5 发现有中断/异常产生,开始处理。对于不同的异常,跳去不同的地址执行程序。这些地址上,只是一条条跳转指令,跳去执行某个函数(地址),这个就是异常向量。
如下就是一个异常向量表,对于不同的异常都有一条跳转指令。

            .globl _start
            _start: b reset // 0
            ldr pc, _undefined_instruction //4
            ldr pc, _software_interrupt //8
            ldr pc, _prefetch_abort //c
            ldr pc, _data_abort //16
            ldr pc, _not_used //20
            ldr pc, _irq // 24 发生中断时,CPU跳到这个地址执行该指令 **假设地址为0x18**
            ldr pc, _fiq
            //我们先在0x18这里放 ldr pc ,__irq,于是cpu最终会跳去执行__irq代码
            //保护现场,调用处理函数,恢复现场
            
6 异常向量表的函数需要: 保存现场(各种寄存器),处理异常(中断),恢复现场。
CPU模式(Mode)、状态(State)与寄存器
对于ARM CPU有7种模式:
usr/sys
undefined(und)
Supervisor(svc)
Abort(abt)
IRQ(irq)
FIQ(fiq)
2种State:
ARM state
Thumb state
寄存器:
CPSR: 当前程序状态寄存器(Current Program Status Register)
CPSR的备份寄存器:SPSR(Save Program Status Register)
我们可以称以下6种为特权模式 privileged mode
sys :系统模式
und :未定义模式
svc :管理模式
abt :终止模式
IRQ :中断模式
FIQ :快中断模式
usr用户模式(不可直接进入其他模式) 可以编程操作CPSR直接进入其他模式 。
01_094 ARM state register.png
这个图是有关各个模式下能访问的寄存器。
寄存器画有灰色的三角形,表示访问该模式下访问的专属寄存器。 比如
mov R0, R8
在System 模式下访问的是R0 ~ R8,在所有模式下访问R0都是同一个寄存器。
mov R0,R8_fiq
但是在FIQ模式下,访问R8是访问的FIQ模式专属的R8寄存器,不是同一个物理上的寄存器。
在这五种异常模式中每个模式都有自己专属的R13 R14寄存器,R13用作SP(栈) R14用作LR(返回地址) LR是用来保存发生异常时的指令地址。
CRSR当前程序状态寄存器,SPSR保存的程序状态寄存器,他们格式如下:
01_095 Program status register format.png
发生异常时,CPU会做如下事情:
1把下一条指令的地址保存在LR寄存器里(某种异常模式的LR等于被中断的下一条指令的地址)
它有可能是PC + 4有可能是PC + 8,到底是那种取决于不同的情况
2 把CPSR保存在SPSR里面(某一种异常模式下SPSR里面的值等于CPSR)
3 修改CPSR的模式为进入异常模式(修改CPSR的M4 ~ M0进入异常模式)
4 跳到向量表
退出异常怎么做?
1 让LR减去某个值,让后赋值给PC(PC = 某个异常LR寄存器减去 offset)
2 把CPSR的值恢复(CPSR 值等于 某一个一场模式下的SPSR)
3 清中断(如果是中断的话,对于其他异常不用设置)
und异常处理函数例子:
            .text
            .global _start

            _start:
            b reset /* vector 0 : reset */
            /*跳转到sdram执行这个函数,那么这个函数一定在sdram中
            我们需要指定让他去前面这块内存去读这个值,担心如果这个文件很大,超过4Knand就没法去读这个文件*/
            ldr pc, und_addr /* vector 4 : und */

            /*增加如下 查看反汇编,在08的地址读然后跳到3c*/
            und_addr:
            .word do_und

            do_und:
            /* 执行到这里之前:
            * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
            * 2. SPSR_und保存有被中断模式的CPSR
            * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
            * 4. 跳到0x4的地方执行程序
            */

            /* sp_und未设置, 先设置它 */
            ldr sp, =0x34000000

            /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
            /* lr是异常处理完后的返回地址, 也要保存 */
            stmdb sp!, {r0-r12, lr}

            /* 保存现场 */
            /* 处理und异常 */
            mrs r0, cpsr
            ldr r1, =und_string
            bl printException

            /* 恢复现场 */
            ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

            und_string:
            .string "undefined instruction exception"
            /**如果你的程序长度稍有变化,就不能保证运行
            加上 .align 4才能保证后面的程序以4字节对齐,保证程序运行
            **/
            .align 4

            reset:
            /* 关闭看门狗 */
            ldr r0, =0x53000000
            ldr r1, =0
            str r1, [r0]

            /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
            /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
            ldr r0, =0x4C000000
            ldr r1, =0xFFFFFFFF
            str r1, [r0]

            /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
            ldr r0, =0x4C000014
            ldr r1, =0x5
            str r1, [r0]

            /* 设置CPU工作于异步模式 */
            mrc p15,0,r0,c1,c0,0
            orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
            mcr p15,0,r0,c1,c0,0

            /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) * m=MDIV+8=92+8=100 * p=PDIV+2=1+2=3 * s=SDIV=1 *
              FCLK=2*m*Fin/(p*2^s)=2*100*12/(3*2^1)=400M */ ldr r0,=0x4C000004 ldr r1,=(92<<12)|(1<<4)|(1<<0) str r1,
              [r0] /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 * 然后CPU工作于新的频率FCLK */ /* 设置内存: sp 栈 */ /* 分辨是nor/nand启动 * 写0到0地址,
              再读出来 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 * 否则就是nor启动 */ mov r1, #0 ldr r0, [r1] /* 读出原来的值备份 */ str
              r1, [r1] /* 0->[0] */
              ldr r2, [r1] /* r2=[0] */
              cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
              ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
              moveq sp, #4096 /* nand启动 */
              streq r0, [r1] /* 恢复原来的值 */

              bl sdram_init
              //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */

              /* 重定位text, rodata, data段整个程序 */
              bl copy2sdram

              /* 清除BSS段 */
              bl clean_bss

              /*把链接地址赋值给pc 直接就跳转到sdram中*/
              ldr pc, =sdram
              sdram:
              bl uart0_init

              bl print1
              /* 故意加入一条未定义指令 */
              und_code:
              .word 0xdeadc0de /* 未定义指令 */
              bl print2

              //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
              ldr pc, =main /* 绝对跳转, 跳到SDRAM */

              halt:
              b halt

            
作者:VanasWu的飞屋环游记
链接:https://www.jianshu.com/p/51e383e1722c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关阅读:
linux基础
linux怎么学
linux和GNU
GNU Free Documentation License
最受欢迎的linux发行版
initroot编辑整理,转载请注明www.initroot.com

100次点赞 100次阅读