本系列文章将以Cortex-M3内核为例,对ARM的异常(exception)进行分析。
文章目录
- 1 异常类型
- 2 优先级
- 3 向量表
1 异常类型
Cortex-M3提供了一个功能丰富的异常体系结构,它支持很多系统异常和外部中断。异常编号1-15表示系统异常,16及以上表示外部中断输入。大多数异常具有可编程优先级,少数具有固定优先级。
| Exception Number | Exception Type | Priority | Description |
|---|---|---|---|
| 1 | Reset | -3(Highest) | Reset |
| 2 | NMI | -2 | Nonmaskable interrupt(external NMI input) |
| 3 | Hard fault | -1 | 如果其它具体的fault handler没有使能,所有的fault都会归为Hard fault |
| 4 | MemManage fault | 可编程 | 内存管理错误;MPU(内存保护单元)非法操作或访问了非法地址 |
| 5 | Bus fault | 可编程 | 总线错误,当AHB接口收到了bus slave的错误响应(指令预取时出错称为prefetch abort,数据访问时出错称为data abort) |
| 6 | Usage fault | 可编程 | 程序中的错误或访问coprocesser(协处理器) |
| 7-10 | Reserved | - | - |
| 11 | SVC | 可编程 | Supervisor Call |
| 12 | Debug monitor | 可编程 | 代码断点、watch window的变量断点和外部调试请求时触发 |
| 13 | Reserved | - | - |
| 14 | PendSV | 可编程 | Pendable Service Call |
| 15 | SYSTICK | 可编程 | System Tick Timer |
| 16 | External Interrupt #0 | 可编程 | - |
| 17 | External Interrupt #1 | 可编程 | - |
| … | … | … | - |
| 255 | External Interrupt #239 | 可编程 | - |
- 前三个异常为负优先级,表示他们的优先级高于其它所有异常
当前正在运行的异常可以通过特殊寄存器IPSR(Interrupt Program Status Register)或者NVIC中断控制状态寄存器(Nested Vectored Interrupt Controllers Interrupt Control State Register)中的VECTACTIVE字段来查看。
- 这里的中断号指的是对Cortex-M3 NVIC的中断输入,在实际的微控制器或SoC中,外部中断输入引脚编号可能与NVIC中的输入编号不同,比如
External Interrupt #0和External Interrupt #1可能会分配给片上外设,剩下的再分配给外部,需查询具体芯片的数据手册
当一个使能的异常产生但不能被立即执行的时候(比如有一个更高优先级的中断服务程序正在处理),它会被挂起(有一个例外:当发生这种情况后,可能会触发Hard fault,具体细节参考 ARM v7-M Architecture Application Level Reference Manual)。也就是说NVIC的挂起寄存器将保存这个异常请求,直到该异常可以被处理为止。
- 即使请求中断的源撤销了这个请求,产生的异常也还是会被处理。
2 优先级
异常是否可以执行以及何时执行与异常的优先级有关,高优先级异常可以抢占低优先级异常。Cortex-M3支持三种固定的最高优先级(Reset/NMI/Hard Fault)和最大256级的可编程优先级(最多128级抢占,因为还分为抢占优先级和子优先级)。但不同的芯片支持的数量不一样,因为Cortex-M3允许设计师通过删除优先级配置寄存器的LSB来减少支持的优先级层数。
- 例:如果芯片仅允许三位优先级,则读0-4位将返回0,写0-4位的任何数值都将被忽略

芯片支持更多的优先级就意味着芯片内部的门电路就更多,从而增加功耗。对于Cortex-M3来说,允许设置的最低优先级层数为8层(3位)。
问:为什么是删除寄存器的LSB而不是MSB
答:方便移植到其它设备,这样具有4位优先级配置寄存器的设备可以直接移植到具有3位优先级配置寄存器的设备上。如果删除MSB,则移植过后可能产生会优先级反转。
优先级寄存器AIRCR(Application Interrupt and Reset Control Register)分为两个部分,前半部分是抢占优先级(preempt priority),后半部分是子优先级(subpriority)。首先来看看AIRCR寄存器中的字段:

| Bits | Name | Type | Reset Value | Description |
|---|---|---|---|---|
| 31:16 | VECTKEY | R/W | - | 访问密钥,必须写为0x05FA,否则对该寄存器的写操作将被忽略 |
| 15 | ENDIANNESS | R | - | 1为大端,0为小端,只能在复位后改变 |
| 10:8 | PRIGROUP | R/W | - | 优先级分组 |
| 2 | SYSRESETREQ | W | - | 通过访问芯片控制逻辑来产生一个复位 |
| 1 | VECTCLRACTIVE | W | - | 清除所有异常的激活状态信息,通常用在debug或OS中以允许系统从错误中恢复 |
| 0 | VECTRESET | W | - | 复位Cortex-M3处理器(除debug部分外),但不会复位处理器外围的电路 |
对于PRIGROUP来说,有8个分组,如下表所示:
| Priority Group | 抢占优先级 | 子优先级 |
|---|---|---|
| 0 | Bit [7:1] | Bit [0] |
| 1 | Bit [7:2] | Bit [1:0] |
| 2 | Bit [7:3] | Bit [2:0] |
| 3 | Bit [7:4] | Bit [3:0] |
| 4 | Bit [7:5] | Bit [4:0] |
| 5 | Bit [7:6] | Bit [5:0] |
| 6 | Bit [7:7] | Bit [6:0] |
| 7 | None | Bit [7:0] |
抢占优先级决定一个中断可以在另一个中断处理程序执行时产生,子优先级仅在抢占优先级相同时使用。当分组为0时,抢占优先级有128级;当分组为7时,异常之间不会发生抢占,除非发生硬件错误(负优先级的异常)。
例:芯片允许三位优先级,优先级分组为5

3 向量表
当异常发生时,Cortex-M3需要知道异常处理程序的地址并执行,而异常处理函数的地址保存在内存中的向量表中。默认情况下,该向量表保存在0地址处。
| Address | Exception Number | Value(Word Size) |
|---|---|---|
| 0x00000000 | - | MSP初始值 |
| 0x00000004 | 1 | Reset Vector(PC的初始值) |
| 0x00000008 | 2 | NMI中断处理函数地址 |
| 0x0000000C | 3 | Hard fault中断处理函数地址 |
| … | … | 其它中断处理函数的地址 |
0地址一般为boot程序,它不能在运行时期修改。但是,向量表可以被重定位到其它内存位置或RAM中,这样我们就可以在运行时期修改。这是通过设置NVIC寄存器中的vector table offset regiser来实现的,如下表所示:
| Bits | NAME | Type | Reset Value | Decription |
|---|---|---|---|---|
| 29 | TBLBASE | R/W | 0 | 向量表基地址在code(0)还是RAM(1) |
| 28:7 | TBLOFF | R/W | 0 | 向量表在code或RAM中的偏移 |
这个offset应该与向量表的大小对齐。
- 由于在boot过程中也可能会发生
NMI或Hard fault异常,而其它的异常只要不使能就不会产生,所以这两个向量表也需要写入零地址。当boot结束后,可以将SRAM中的一段内存定义为新的向量表的基地址
例:我们有32个中断向量,加上系统默认的16个异常共48个。首先要2字节对齐到64,接着一个向量表地址占4字节,即最后这个偏移量应该设置为256(0x100)。





![[hadoop全分布部署]安装Hadoop、配置Hadoop 配置文件②](https://img-blog.csdnimg.cn/0cbfd041ee58405fb8b9e0b983ace87c.png)












