1 前言
Cortex-M7集成了嵌套向量中断控制器(Nested Vectored Interrupt Controller),最大支持256个中断优先级,其 “向量” 指示着中断服务函数(interrupt service routines, ISRs)响应的方式不再通过软件编程来实现,即由硬件堆叠的寄存器组和相应可挂起的多指令加载和存储指令(可被中断打断,给中断让路),来实现从中断响应到ISR入口的快速选择这一过程。
2 异常模型(Exception model)
2.1 异常状态(Exception states)
状态 | 描述 |
Inactive | 该异常非active,且没有pending |
Pending | 该异常正在等待CPU响应 |
Active | CPU已经开始处理该异常,且尚未结束(当一个异常打断另一个异常处理时,则认为这两个异常都处于Active状态) |
Active and pending | 该异常已被CPU处理,同时又有同一中断源的新异常发生,该新异常被悬起 |
2.2 异常类型(Exception Types)
Cortex-M7的中断向量表最大支持256个异常,并主要包含以下异常类型:
2.2.1 Reset
Reset是一种特殊的异常类型,在系统上电或热复位后执行,其优先级固定为-3(全局最高,会得到最优先的响应),且永远处于使能状态,谁也无法剥夺一个系统重启的权利;当Reset被触发时,CPU会放下手头正在做的任何事情,立即响应,并从中断向量表中找到对应的中断服务函数(ISR),即Reset_Handler,重启完成后进入特权级线程模式;
通常来说,从复位开始,到重启开始,这之间通常还有一个过程,而Reset_Handler则是在重启后运行并完成初始化过程的。
2.2.2 NMI
non-maskable interrupt,不可屏蔽中断,其优先级固定为-2,仅次于Reset,NMI的使用取决于用户,通常会用于看门狗、安全警报、调试等场景。
2.2.3 Hardfault
Hardfault是由异常处理过程中的错误引发的异常,或者是由一个其他异常机制处理不了的问题引起。其优先级固定为-1,也就是说排名老三,是最后一个优先级不可配置(为固定值)的异常。
2.2.4 MemManage
通常是由内存保护相关的错误引起的异常。通常由固定的内存保护规则或MPU来定义这种错误,该错误通常用于种植对非执行内存区域(Excute Never, XN)的指令访问。总之,对特定内存区域的违规操作,通常会通过MemManage异常来进行处理。
2.2.5 BusFault
通常是由数据或指令处理过程中,内存系统总线上发生了错误,而导致该异常。
2.2.6 UsageFault
通常是由于非法的指令的执行导致,包括:
① 一个未定义的指令;
② 非法的未对齐访问;
③ 指令执行的无效状态;
④ 异常返回时发生错误;
例如,除0操作或在字或半字对齐的存储器上访问未对齐的地址,都会导致该异常。而Cortex-M7虽然支持非对齐访问,但也只局限于以下指令:
• LDR, LDRT.
• LDRH, LDRHT.
• LDRSH, LDRSHT.
• STR, STRT.
• STRH, STRHT
其余的非对其访存都会引发UsageFault,而且通常非对齐访存的效率都会降低,所以变成过程中还是要养成内存对齐的习惯。
2.2.7 SVCALL
Supervisor call(SVC),由SVC指令触发。在OS环境下,应用程序可以通过SVC指令陷入内核进行系统调用或访问设备驱动层。
2.2.8 PendSv
PendSv通常用于OS的任务(线程)切换,并设置其优先级为最低,以保证不会抢占其他异常的处理过程,即在其他异常处理完成后运行;
2.2.9 SysTick
当产生系统时钟。
2.2.10 Interrupt(IRQ,中断请求)
通常由外设或软件请求产生的异常,通常这种异常是由外部信号或软件向CPU的请求产生,所以通常称之为外部中断。所以,外部中断对于CPU当前的指令执行来说都是异步的,也就是说CPU不知道它何会来,来了就是个情理之中的不速之客。因此,外部中断,可以理解为外设与CPU通信的手段。
总的来看:
① 除了Reset,NMI和HardFault这三个优先级为固定负数值的大佬外,其余异常的优先级都是可配置的;
② 除了MemManage,UsageFault等CPU可以自己检测发现的错误引发的异常,以及SVCall
这种通过显式指令执行引发的异常,绝大部分异常对于CPU来说都是异步的。所谓,兵来将挡,水来土掩,CPU按优先级对发生的异常一一处理,若多个异常同时发生,则将排队中的异常悬起等待;
③对于异步的异常,除了Reset之外,处理器在异常触发和进入对应的异常处理函数(ISR)前,尚有一条指令的时间处理其余事情(想必是用于收拾行李吧)。
2.3 异常处理
异常处理主要包括三种:
①System handlers,主要是NMI,PendsV, SVcall等系统自定义的16个异常(MSP除外,其实是15个);
②Interrupt Service Routines(ISRs),中断服务函数,主要是指外部中断的处理函数,通常由用户自定义,最多支持240个,实际支持的数量取决与具体的CPU设计。
3 中断向量表
如图1所示,所谓中断向量表,其代码层看起来就是个一位数组,其元素为32位的地址。第一个元素比较特殊,为复位后MSP的初始地址(值),其余255个都是中断服务函数(ISR)的入口地址。
通常来说,系统上电后,会在一个已知的固定地址找到这个向量表,从而获取MSP的初始值(也就是栈的初始地址),在后续中断发生的时候,根据向量表的初始地址(数组的初始地址),就可以定位到ISR的入口地址,进而控制PC跳过去。
此外,由于只支持在Thumb下执行指令,在EPSR 的T字段为0的情况下,执行任何指令都将导致错误或锁定,而向量表中地址都是要写入PC的。所以,向量表中的这些地址的bit[0]必须是1,这个1会最终体现在EPSR 寄存器的T字段。此外,如果这个向量表中填的是ISR的函数指针,且编译器设置选项中设置了Thumb-2指令模式,则编译器会保证bit[0]为1;如果是手动填入的地址,则需要应用程序自己来保证。
图1 中断向量表(Vector table)
4 中断相关的程序状态寄存器
程序状态寄存器(Program Status Register, PSR)属于特殊功能寄存器,包括以下三个子状态寄存器:
• Application Program Status Register (APSR);
• Interrupt Program Status Register (IPSR);
• Execution Program Status Register (EPSR)
图2 PSR寄存器位域分布
其位域分布如图2所示,通过 MRS/MSR 指令,这 3 个 PSRs 即可以单独访问,也可以组合访问(2 个组合, 3 个组合都可以)。当使用三合一的方式访问时,应使用名字“xPSR”或者“PSR”,具体如图3所示。
图3 PSR寄存器组合(combinations)说明
在这三个子状态寄存器种,IPSR和EPSR都与中断有着直接的关系。CPU响应异常的第一步就是寄存器压栈(保护现场),包括xPSR, PC, LR, R12以及R3-R0都是由硬件自动完成压栈,同时指令总线(I-CODE)总线会进行中断向量的取指工作,即找到ISR入口地址。在此基础上,还需要更新SP(使用MSP)、IPSR的ISR_NUMBER、PC(中断向量取指完成后指向ISR入口地址)、LR(存放EXC_RETURN)同时还会伴随着NVIC中响应异常的悬起位的清除与活动位的置位。
4.1 中断程序状态寄存器IPSR
IPSR(Interrupt Program Status Register)记录了当前正在执行的ISR(Interrupt Service Routine)对应的异常号。该寄存器属于特殊功能寄存器,仅在特权模式下只读(不可写),非特权模式更是想都不要想,其位域分布如图4所示:
图4 IPSR寄存器位域分布
注意,图4中的ISR_NUMBER为中断异常号,代表的是该异常在中断向量表中索引,包含CPU自身的16个系统中断(姑且将MSP也算在内)和最多240个外部中断。 当ISR_NUMBER为0时,表示当前处于线程模式,而非异常处理过程。
4.2 执行程序状态寄存器EPSR
EPSR(Execution Program Status Register)记录了程序执行种的状态信息,包括Thumb状态位,IT(if-then)指令块和可中断的连续指令(Interruptible-Continuable Instruction ,ICI)的执行状态位,具体位域分布如下:
Bits | Name | 功能 |
31~27 | -- | Reserved |
26~25,15~10 | ICI/IT | 可中断的连续指令/IT指令块的执行状态位 |
24 | T | Thumb状态位 |
23~16,9~0 | -- | Reserved |
其中,bit26~25,15~10指示的是指令的执行状态,由于IT指令块和ICI指令包含一系列连续的操作,若在执行过程种被异常打断,则在进入异常处理序列前,该执行状态位也参与到异常响应前的现场保护中去,并在异常返回时,结合LR中存储的信息恢复现场,继续指令的执行。