1、Exception model
每个异常(Exception)都有一个关联的Exception number和一个关联的Priority number。Priority Number越小,优先级越高,Reset Exception的Priority number是-4,当AIRCR.BFHFNMINS等于1,Secure HardFault的是-3,AIRCR.BFHFNMINS等于0,Secure HardFault的是-1,Non-secure HardFault的也是-1,其余的Priority number都是可配的。在可配的exception priority number中,SHPR1- SHPR3用于控制内部exception的priority,NVIC_IPRn.PRI_<n>用于控制外部exception的priority,可配置的priority number最低值为0(最高优先级)。
具体可配置的priority number个数是实现定义的,对于有Main Extension的PE来说,范围是8~256个(2的幂次方),反之,为4个。Exception number的前15个用于PE内部的Exception,其余的用于External interrupt。对于PE来说,支持的external interrupt数目是可配置的,最大496个。
对每个Exception来说,有相应的enable、pending和active的状态位。当exception发生时,它的pending状态位会设置为1,当exception被处理时,它的pending状态位会变为0,而active状态位会变为1。可通过enable来设置是否使能某exception,不过Secure HardFault、NMI、SVCall和PendSV总是使能的,因此不需要enable控制位。
软件触发中断
Interrupt不仅仅可以通过硬件触发,也可以使用软件来触发,具体有:
使用privileged执行去写NVIC_ISPRn器;
CCR.USERSETMPEND为1,unprivileged执行去写STIR寄存器
不过也只有interrupt可以使用软件触发(可能是方便软件调试吧),exception number中前15个是不能通过软件触发,它们是PE内部自己产生的。对于这几个PE内部的exception,还有额外的status bit指示具体是哪个地方引起的exception,这些status bit是分布在HFSR、MMFSR、BFSR、UFSR、SFSR(这些应该简称为XFSR)。
2、Priority model
除了reset之外,异常(exception)有以下几种可能的状态:
Active:
一个exception处于要么:
正在被处理中
正在被处理,但中断处理程序被更高优先级异常的中断处理程序抢占
Pending:一个exception已经生成,但未处于active状态
Inactive:Exception还没有生成
Active and pending:
该exception的前一个仍然处于active状态,后一个处于pending状态,因此只有asynchronous exception可以时active and pending状态。Synchronous exceptions要么是inactive, pending或active状态的。
Priority number越低,优先级越高。正在执行的指令流可以被具有足够高优先级的exception打断。即exception的priority number低于当前组的priority number。
Mask寄存器
PRIMASK, BASEPRI, FAULTMASK这三个mask寄存器可以用于改变priority的配置。
PRIMASK
当PE没有security extension时,如果PRIMASK等于1,会将当前execution priority提升到0,并屏蔽掉具有同等或更低priority的所有exception。
当PE具有security extension时,如果PRIMASK_S等于1,会将当前execution priority提升到0。如果PRIMASK_NS设置为1,AIRCR.PRIS为0时,当前execution priority提升到0;AIRCR.PRIS为1时,当前execution priority提升到0x80。当前execution priority提升到特定值时,所有具有相同或更低priority的exception都将被屏蔽。
FAULTMASK
当PE没有security extension时,将FAULTMASK设置为1会将当前的execution priority提高到-1,并屏蔽所有具有相同或更低优先级的exception。
当PE具有security extension时,如果AIRCR.BFHFNMINS为:
0:
设置FAULTMASK_S为1会使当前execution priority提升到-1。如果AIRCR.PRIS为0:
设置FAULTMASK_NS为1会使得当前execution priority提升到0;
设置FAULTMASK_NS为0会使得当前execution priority提升到0x80;
1:
设置FAULTMASK_S为1会使得当前execution priority提升到-3;设置FAULTMASK_NS为1会使得当前execution priority提升到-1;
当前execution priority提升到特定值时,所有具有相同或更低priority的exception都将被屏蔽。
BASEPRI
当PE没有security extension时,该字段可以设置为1~最大支持的priority number之间的一个值。这会将当前execution priority提高到该数字,从而屏蔽相同或更低priority的exception。
当PE具有security extension时,BASEPRI_S可以设置为1~最大支持的priority number之间的一个值。当AIRCR.PRIS为0时,BASEPRI_NS可以设置为1~最大支持的priority number之间的一个值;当AIRCR.PRIS为1时,BASEPRI_NS可以设置为1~最大支持的priority number之间的一个值,但是BASEPRI_NS中的值将会映射到priority范围的下半部分,以便当前execution priority提升到priority范围下半部分的映射值,即从0x80到支持的最大值。
当前execution priority提升到特定值时,所有具有相同或更低priority的exception都将被屏蔽。
PRIMASK、FAULTMASK和BASEPRI优先级提升机制只能提升group priority,不能提升subpriority。
PRIMASK和FAULTMASK可以通过执行CPS指令来设置。当满足以下任一条件时,保证指令的效果是可见的:
如果执行了CPSID变体,PE将对更改进行序列化,以便在CPSID指令完成后出现在program order之后的指令可以看到它;
如果执行了CPSIE变体,则只保证该指令的效果在CSE事件之后就可见;
3、Exception entry/retrun
Exception entry
在Exception entry时会进行context堆栈,Arm通常是采用Full Descending的堆栈方式。Exception堆栈的范围可以分为以下四个部分:
State context registers.
Additional state context registers.
floating-point context registers.
Additional floating-point context registers
在exception entry时,state context registers总是会被堆栈的,当然除了tail-chaining场景。其它部分是否存在取决于执行的exception转换的类型以及是否存在floating-point条件。除非另有说明,如果某一个部分不堆栈,则其它部分向上移动以减少所需的堆栈空间。
堆栈可以下降到它的堆栈限制值(Stack limit)。任何试图将堆栈下降到超过其堆栈限制值的地方都是违反堆栈限制的。如果违反堆栈限制,则会:
生成同步的STKOF UsageFault。
堆栈指针设置为堆栈限制值。
不执行低于堆栈限制值的地址的Push操作。
是否执行对等于或高于堆栈限制值的地址的推送操作是由IMPLEMENTATION DEFINED决定的。
堆栈限制检查在创建stack frame期间执行,用于以下所有情况:
Exception entry。
从安全异常到非安全异常的tail-chaining。
从安全代码到非安全代码的函数调用。
Exception return
当以下两个条件都为成立时,PE开始exception return:
PE处于handler模式。
以下指令之一将EXC_RETURN值(0xFFXXXXXX)加载到PC中:
POP (multiple registers)或LDM中包括了加载PC
将PC作为目的寄存器的LDR
具有任何寄存器的BX
具有任何寄存器的BXNS
当这两个条件都为真时,在PC中检测到EXE_RETURN值时,PE开始将exception stack frame进行出栈,并继续恢复执行出栈的上下文。
当满足以下任何条件时,加载到PC中的EXC_RETURN值将被视为地址:
该值由列出的指令以外的指令加载。
通过vector fetch从vector表中加载该值。
该值在PE处于Thread模式时加载。
Exceptions during exception entry
在exception entry期间,有可能会发生exception,例如异步异常,或者exception entrysequence执行本身也可能导致exception,例如在往stack中push时出现MemManage错误。
在exception entry期间发生的任何exception都是late-arriving exception,并且:
导致原来的exception entry的是original exception。
在original exception发生时运行的代码流的优先级是为preempted priority。
当exception entry序列本身导致exception时,后一个exception叫作derived exception。
在exception entry期间,如果late-arriving exception发生且具有较高的优先级,那么PE进入late-arriving exception,这个机制称作late-arrival preemption。
在late-arrival preemption中:
late-arriving exception采用original exception的exception entry sequence。Original exception保持pending状态;
PE从late-arriving exception返回后,再进行original exception;
在late-arrival preemption中产生derived exception时,PE忽略非致命faults;
Exceptions during exception return
在exception return期间可能发生exception,例如异步异常,或者exception return本身可能导致异常。
在exception return期间发生的任何exception都是late-arriving exception。
当exception return sequence本身导致exception时,后一个exception叫作derived exception。
当在exception return期间,late-arriving exception的priority 值低于要返回执行的priority值时(prioriry值越低权限越高),PE通过tail-chaining来执行late-arriving exception。
如果在exception return期间,derived exception的priority值低于要返回执行的priority值,PE将使用tail-chaining来执行derived exception。
4、Tail-chaining
Tail-chaining行为如下:
在PC中检查到EXC_RETURN值时,如果出现pending的exception或derived exception,且它们的group priority值低于要返回的execution priority值,那么PE会:
不继续unstack exception task frame了;
执行pending exception或derived exception;
当tail-chaining发生时,PE不会执行任何已经被original exception抢占的后台指令;
Tail-chaining消除了unstack和restack操作。下面的示意图中,第二个exception就是tail-chained exception:
当EXC_RETURN.S指示后台是secure state,也就是exception发生之前的状态时secure state。如果tail-chaining导致security状态从secure变化到non-secure,且EXC_RETURN.DCRS为1,那么在进行non-secure exception时,额外的context需要被保存到栈中。
当一个exception是tail-chained,并且返回的exception是secure的,EXC_RETURN.DCRS用于跟踪是否有额外的context放到堆栈中,如下图所示。
5、instruction resume/restart
interrupt-continuable instructions
多周期load或store指令可以在执行过程中插入中断,等中断服务程序完成后,继续执行该指令剩余部分,而不需要从头重新执行该指令。多周期load或store指令的中断会使PE设置EPSR.ICI字段。
interrupt-continuable instructions有:
LDM.
LDMDB.
STM.
STMDB.
POP (multiple registers).
Push (multiple registers).
在具有Floating-point扩展的PE中,floating-point interrupt-continuable instructions有:
VLDM.
VSTM.
VPOP.
VPUSH.
当PE使用instruction resume时,EPSR.ICI会设置未非0值,表示interrupt-continuable instruction的延续状态:
对于LDM, LDMDB, STM, STMDB, POP (multiple registers), PUSH (multiple registers)和CLRM指令,EPSR.ICI包含在instruction resume后要load、store或clear的寄存器列表中的第一个寄存器的编号;
对于floating-point指令,VLDM, VSTM, VPOP, VPUSH和VSCCLRM指令,EPSR.ICI包含在PE处理exception之前未load、store或clear的doubleword floating-point extension register的最低编号。
PE可以在执行interrupt-continuable instruction期间去处理exception,这是它会暂停执行该指令,并在exception return后恢复该指令的执行,这称为instruction resume。Instruction resume是一个可选的优化。
PE可以放弃interrupt-continuable instruction的执行去处理exception,并在从exception return后,从该指令的开始处重新启动该interrupt-continuable instruction,这称为instruction restart。如果PE使用instruction restart方案,Arm建议不要对device memory中的数据使用Load Multiple或Store Multiple指令。
exception-continuable instructions
如果实现了MVE,则体系结构支持在多个部分执行的指令中间中断beat-wise指令,这些指令是exception-continuable instructions。exception-continuable instructions可以使PE设置EPSR.ECI字段。
MVE将vector operations划分为很多beats。架构允许同时执行来自多条指令的beats。在vector operation的任何beat之后都可以出现exception,这时候写入EPSR.ECI的值会用于指示vector operation的哪个beats已经完成了,这样方便exception return后,继续执行剩余的beats。
由于架构是跟踪vector instructions的beats完成情况,而element size可以小于beat大小,因此可能会出现有部分完成的beat生成exception。
Vector load或store指令可能会放弃并接着重启一个beat的执行,这可能会导致对同一memory位置的多次访问。
历史系列文章:
150
