<strong>1、摘要 </strong>
Cortex-M内核实现了一个高效异常处理模块,可以捕获非法内存访问和数个程序错误条件。本应用笔记从程序员角度描述Cortex-M Fault异常,并且讲述在软件开发周期中的Fault用法。
<strong>2、简介 </strong>
Cortex-M3(以下简称CM3)和Cortex-M4(以下简称CM4)内核的Fault异常可以捕获非法内存方法和非法编程行为。Fault异常能够检测到以下情况:
总线Fault:在取址、数据读/写、取中断向量、进入/退出中断时寄存器堆栈操作(入栈/出栈)时检测到内存访问错误。
存储器管理Fault:检测到内存访问违反了MPU定义的区域。
用法Fault:检测到未定义的指令异常,未对齐的多重加载/存储内存访问。如果使能相应控制位,还可以检测出除数为零以及其他未对齐的内存访问。
硬Fault:如果上面的总线Fault、存储器管理Fault、用法Fault的处理程序不能被执行(例如禁能了总线Fault、存储器管理Fault、用法Fault异常或者在这些异常处理程序执行过程中又出现了Fault)则触发硬Fault。
本应用笔记描述CM3和CM4的Fault异常用法。系统控制寄存器组中的寄存器可以控制Fault异常或者提供引发异常的原因信息。
<strong>更深入的文档 </strong>
完整的异常描述见《Cortex - M3 Technical Reference Manual》或者《Cortex -M4 Technical Reference Manual》,这两本参考手册都可以在www.arm.com中找到。
另一个很好的参考书是由Joseph Yiu编写的《The Definitive Guide to the ARM Cortex-M3》 (这本书有中文版:宋岩译的《ARM Cortex-M3权威指南》)。
<strong>3、Cortex-M Fault异常和寄存器</strong>
每个符合CMSIS规范的编译器所提供的启动文件(Startup_device)都会定义好设备所有的异常和中断向量。这些向量表定义了异常或中断处理程序的入口地址。下表给出了一个典型的向量表,Fault异常向量用蓝色标注。
:
:
__Vectors DCD __initial_sp ; 栈顶
DCD Reset_Handler ; 复位处理程序入口
DCD NMI_Handler ; NMI 处理程序入口
DCD HardFaul t_Handler ; 硬Fault处理程序入口
DCD MemManage_Handler ; 存储器管理处理程序入口
DCD BusFault_Handler ; 总线Fault 处理程序入口
DCD UsageFault_Handler ; 用法 Fault 处理程序入口
DCD 0 ; 保留
:
:
通常总是使能硬Fault异常的,硬Fault异常具有固定的优先级,并且优先级高于其它Fault异常以及中断,但低于NMI。硬Fault异常处理程序在以下情况下会被执行:其它非硬Fault异常(非硬Fault异常是指总线、存储器管理和用法Fault 异常,下同。)被禁能,并且这些Fault异常被触发;在执行一个非硬Fault异常处理程序中又产生非硬Fault异常。
所有非硬Fault具有可编程的优先级。当Cortex-M内核复位后,这些非硬Fault被禁能,你可以在应用软件中通过设置“系统Handler控制及状态寄存器(SHCSR)”来使能非硬Fault异常。这个寄存器属于系统控制模寄存器组(SCB)
<strong>3.1 Fault异常的控制寄存器</strong>
在这里有必要介绍一下系统控制模块寄存器组(SCB)的成员,这个寄存器组的定义可以在core_cm3.h文件中,该文件属于CMSIS Cortex-M3 内核外设接口抽象层的一部分(关于不清楚CMSIS的,可以自行查找资料)。定义如下:
(1)定义系统控制寄存器组结构体
/** @brief System Control Block (SCB) register structure definition */
typedef struct
{
__I uint32_t CPUID; /*!< Offset: 0x00 CPU ID Base Register*/
__IO uint32_t ICSR; /*!< Offset: 0x04 Interrupt Control State Register*/
__IO uint32_t VTOR; /*!< Offset: 0x08 Vector Table Offset Register*/
__IO uint32_t AIRCR; /*!< Offset: 0x0C Application Interrupt / Reset Control Register*/
__IO uint32_t SCR; /*!< Offset: 0x10 System Control Register*/
__IO uint32_t CCR; /*!< Offset: 0x14 Configuration Control Register*/
__IO uint8_t SHP[12]; /*!< Offset: 0x18 System Handlers Priority Registers (4-7, 8-11, 12-15) */
__IO uint32_t SHCSR; /*!< Offset: 0x24 System Handler Control and State Register */
__IO uint32_t CFSR; /*!< Offset: 0x28 Configurable Fault Status Register*/
__IO uint32_t HFSR; /*!< Offset: 0x2C Hard Fault Status Register*/
__IO uint32_t DFSR; /*!< Offset: 0x30 Debug Fault Status Register */
__IO uint32_t MMFAR; /*!< Offset: 0x34 Mem Manage Address Register*/
__IO uint32_t BFAR; /*!< Offset: 0x38 Bus Fault Address Register*/
__IO uint32_t AFSR; /*!< Offset: 0x3C Auxiliary Fault Status Register*/
__I uint32_t PFR[2]; /*!< Offset: 0x40 Processor Feature Register*/
__I uint32_t DFR; /*!< Offset: 0x48 Debug Feature Register*/
__I uint32_t ADR; /*!< Offset: 0x4C Auxiliary Feature Register*/
__I uint32_t MMFR[4]; /*!< Offset: 0x50 Memory Model Feature Register*/
__I uint32_t ISAR[5]; /*!< Offset: 0x60 ISA Feature Register*/
} SCB_Type;
(2) 定义系统控制寄存器组物理空间基地址
(3) 定义指向系统控制寄存器组的指针
#define SCB ((SCB_Type *)SCB_BASE) /*!< SCB configuration struct * /
通过以上三步,我们就可以使用结构体指针SCB来访问系统控制寄存器组的寄存器了,比如给系统控制寄存器SCR赋值:SCB->SCR=0xFF;
SCB->CCR寄存器控制除数为零和未对齐内存访问是否触发用法Fault。
SCB->SHCSR寄存器可用来使能非硬Fault异常。如果一个非硬Fault异常被禁能并且相关Fault发生,这时异常会升级为硬Fault。SCB->SHP寄存器组控制异常的优先级。
Fault异常控制寄存器列表:
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="90"><p>地址/访问</p></td>
<td valign="top" width="99"><p>寄存器</p></td>
<td valign="top" width="89"><p>复位值</p></td>
<td valign="top" width="419"><p>描述</p></td>
</tr>
<tr>
<td width="91"><p>0xE000ED14</p>
<p>RW 特权级</p></td>
<td width="99"><p>SCB->CCR</p></td>
<td width="89"><p>0x00000000</p></td>
<td width="418"><p>配置和控制寄存器:包含控制除数为零和未对齐内存访问是否触发用法Fault的使能位。</p></td>
</tr>
<tr>
<td width="92"><p>0xE000ED18</p>
<p>RW 特权级</p></td>
<td width="99"><p>SCB->SHP[12]</p></td>
<td width="89"><p>0x00</p></td>
<td width="417"><p>系统处理程序优先级寄存器:控制异常处理程序的优先级</p></td>
</tr>
<tr>
<td width="93"><p>0xE000ED24</p>
<p>RW特权级</p></td>
<td width="99"><p>SCB->SHCSR</p></td>
<td width="89"><p>0x00000000</p></td>
<td width="417"><p>系统处理程序控制和状态寄存器</p></td>
</tr>
</tbody>
</table>
<strong>3.1.1 SCB->CCR 寄存器</strong>
蓝色部分控制是否使能相应的用法Fault
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="68"><p><a name="_Hlk350248768" id="_Hlk350248768"></a>位</p></td>
<td valign="top" width="115"><p>名称</p></td>
<td valign="top" width="513"><p>描述</p></td>
</tr>
<tr>
<td width="69"><p>[31:10]</p></td>
<td width="115"><p>-</p></td>
<td width="513"><p>保留</p></td>
</tr>
<tr>
<td width="70"><p>[9]</p></td>
<td width="115"><p>STKALIGN</p></td>
<td width="512"><p>表示进入异常时的堆栈对齐。</p>
<p>0:4字节对齐</p>
<p>1:8字节对齐</p>
<p>进入异常时,处理器使用压入堆栈的PSR位[9]来指示堆栈对齐。从异常返回时,这个堆栈位被用来恢复正确的堆栈对齐。</p></td>
</tr>
<tr>
<td width="71"><p>[8]</p></td>
<td width="115"><p>BFHFNMIGN</p></td>
<td width="511"><p>使能时,使得以优先级位-1或-2运行的处理程序忽略加载和存储指令引起的数据总线故障。它用于硬故障、NMI和FAULTMASK升级处理程序中:</p>
<p>0:加载和存储指令引起的数据总线故障会引起锁定。</p>
<p>1:以优先级-1或-2运行的处理程序忽略加载和存储指令引起的数据总线故障。</p>
<p>仅在处理程序和其数据处于绝对安全的存储器时将该位设为1。一般将该位用于探测系统设备和桥接器以检测并纠正控制路径问题。</p></td>
</tr>
<tr>
<td width="72"><p>[7:5]</p></td>
<td width="115"><p>-</p></td>
<td width="510"><p>保留</p></td>
</tr>
<tr>
<td width="73"><p>[4]</p></td>
<td width="115"><p>DIV_0_TRP</p></td>
<td width="510"><p>当处理器进行除0操作(SDIV或UDIV指令)时,会导致故障或停止。</p>
<p>0:不捕获除以零故障</p>
<p>1:捕获除以零故障。</p>
<p>当该位设为0时,除以零返回的商数为0。</p></td>
</tr>
<tr>
<td width="74"><p>[3]</p></td>
<td width="114"><p>UNALIGN_TRP</p></td>
<td width="509"><p>使能非对齐访问捕获:</p>
<p>0:不捕获非对齐半字和字访问</p>
<p>1:捕获非对齐半字和字访问。</p>
<p>如果该位设为1,非对齐访问产生一个使用故障。无论UNALIGN_TRP是否设为1,非对齐的LDM、STM、LDRD和STRD指令总是出错。</p></td>
</tr>
<tr>
<td width="75"><p>[2]</p></td>
<td width="114"><p>-</p></td>
<td width="508"><p>保留</p></td>
</tr>
<tr>
<td width="76"><p>[1]</p></td>
<td width="114"><p>USERSETM</p>
<p>PEND</p>
<p> </p></td>
<td width="507"><p>使能对STIR的无特权软件访问。</p>
<p>0:禁能</p>
<p>1:使能</p></td>
</tr>
<tr>
<td width="76"><p>[0]</p></td>
<td width="114"><p>NONEBASE</p>
<p>THRDENA</p>
<p> </p></td>
<td width="507"><p>指示处理器如何进入线程模式:</p>
<p>0:处理器仅在没有有效异常时才能够进入线程模式。</p>
<p>1:处理器可以从EXC_RETURN值控制下的任何级别进入线程模式</p></td>
</tr>
</tbody>
</table>
<strong>3.1.2 SCB->SHP 寄存器组</strong>
以下SCB->SHP 寄存器组的寄存器用来设置异常处理程序的优先级:
SCB->SHP[0]:存储器管理Fault的优先级
SCB->SHP[1]:总线Fault的优先级
SCB->SHP[2]:用法Fault的优先级
为了编程中断和异常的优先级,CMSIS提供了函数NVIC_SetPrioriity和NVIC_GetPriority。这两个函数也位于core_cm3.h中,源码为:
可以通过下面的示例代码更改异常优先级:
:
:
NVIC_SetPriority (MemoryManagement_IRQn, 0xF0);
NVIC_SetPri ority (BusFault_IRQn, 0x80);
NVIC_SetPriority ( UsageFault_IRQn, 0x10);
:
UsageFault_prio = NVIC_GetPriority ( UsageFault_IRQn);
:
:
<strong>3.1.3 SCB->SHCSR寄存器</strong>
与Fault异常相关位见下表的蓝色部分
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="62"><p>位</p></td>
<td valign="top" width="154"><p>名称</p></td>
<td valign="top" width="483"><p>描述</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[31:19]</p></td>
<td valign="top" width="154"><p>-</p></td>
<td valign="top" width="483"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[18]</p></td>
<td valign="top" width="154"><p>USGFAULTENA</p></td>
<td valign="top" width="483"><p>用法Fault使能位,设为1时使能</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[17]</p></td>
<td valign="top" width="154"><p>BUSFAULTENA</p></td>
<td valign="top" width="483"><p>总线Fault使能位,设为1时使能</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[16]</p></td>
<td valign="top" width="154"><p>MEMFAULTENA</p></td>
<td valign="top" width="483"><p>存储器管理Fault使能位,设为1使能</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[15]</p></td>
<td valign="top" width="154"><p>SVCALLPENDED</p></td>
<td valign="top" width="483"><p>SVC调用挂起位,如果异常挂起,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[14]</p></td>
<td valign="top" width="154"><p>BUSFAULTPENDED</p></td>
<td valign="top" width="483"><p>总线Fault异常挂起位,如果异常挂起,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[13]</p></td>
<td valign="top" width="154"><p>MEMFAULTPENDED</p></td>
<td valign="top" width="483"><p>存储器Fault故障异常挂起位,如果异常挂起,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[12]</p></td>
<td valign="top" width="154"><p>USGFAULTPENDED</p></td>
<td valign="top" width="483"><p>用法Fault异常挂起位,如果异常挂起,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[11]</p></td>
<td valign="top" width="154"><p>SYSTICKACT</p></td>
<td valign="top" width="483"><p>SysTick 异常有效位,如果异常有效,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[10]</p></td>
<td valign="top" width="154"><p>PENDSVACT</p></td>
<td valign="top" width="483"><p>PendSV异常有效位,如果异常有效,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[9]</p></td>
<td valign="top" width="154"><p>-</p></td>
<td valign="top" width="483"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[8]</p></td>
<td valign="top" width="154"><p>MONITORACT</p></td>
<td valign="top" width="483"><p>调试监控有效位,如果调试监控有效,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[7]</p></td>
<td valign="top" width="154"><p>SVCALLACT</p></td>
<td valign="top" width="483"><p>SVC调用有效位,如果SVC调用有效,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[6:4]</p></td>
<td valign="top" width="154"><p>-</p></td>
<td valign="top" width="483"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[3]</p></td>
<td valign="top" width="154"><p>USGFAULTACT</p></td>
<td valign="top" width="483"><p>用法Fault异常有效位,如果异常有效,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[2]</p></td>
<td valign="top" width="154"><p>-</p></td>
<td valign="top" width="483"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[1]</p></td>
<td valign="top" width="154"><p>BUSFAULTACT</p></td>
<td valign="top" width="483"><p>总线Fault异常有效位,如果异常有效,该位读为1</p></td>
</tr>
<tr>
<td valign="top" width="62"><p>[0]</p></td>
<td valign="top" width="154"><p>MEMFAULTACT</p></td>
<td valign="top" width="483"><p>存储器管理Fault异常有效位,如果异常有效,该位读为1</p></td>
</tr>
</tbody>
</table>
尽管可以写SCB->SHCSR寄存器的所有位,但建议软件只写异常使能位。下面的例子用于使能所有非硬Fault(存储器管理Fault、总线Fault、用法Fault异常):
SCB - >SHCSR |= 0x00007000; /*enable Usage Fault, Bus Fault, and MMU Fault*/
注:要包含core_cm3.h头文件。
<strong>3.2 Fault异常的状态和地址寄存器</strong>
Fault状态寄存器组(SCB->CFSR和SCB->HFSR)和Fault地址寄存器组(SCB->MMAR和SCB->BFAR)包含Fault的详细信息以及异常发生时访问的内存地址。
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="107"><p>地址/访问</p></td>
<td valign="top" width="121"><p>寄存器</p></td>
<td valign="top" width="89"><p>复位值</p></td>
<td valign="top" width="381"><p>描述</p></td>
</tr>
<tr>
<td width="107"><p>0xE000ED28</p>
<p>RW 特权级</p></td>
<td width="121"><p>SCB->CFSR</p></td>
<td width="89"><p>0x00000000</p></td>
<td width="381"><p>可配置Fault状态寄存器:包含指示存储器管理Fault、总线Fault或用法Fault的原因位</p></td>
</tr>
<tr>
<td width="107"><p>0xE000ED2C</p>
<p>RW 特权级</p></td>
<td width="121"><p>SCB->HFSR</p></td>
<td width="89"><p>0x00000000</p></td>
<td width="381"><p>硬Fault状态寄存器:包含用于指示硬Fault原因位。</p></td>
</tr>
<tr>
<td width="107"><p>0xE000ED34</p>
<p>RW特权级</p></td>
<td width="121"><p>SCB->MMFAR</p></td>
<td width="89"><p>不可知</p></td>
<td width="381"><p>存储器管理Fault地址寄存器:包括产生存储器管理Fault的位置的地址</p></td>
</tr>
<tr>
<td width="107"><p>0xE000ED38</p>
<p>RW特权级</p></td>
<td width="121"><p>SCB->BFAR</p></td>
<td width="89"><p>不可知</p></td>
<td width="381"><p>总线Fault地址寄存器:包括产生总线Fault的位置的地址</p></td>
</tr>
</tbody>
</table>
<strong>3.2.1 SCB->CFSR寄存器</strong>
SCB->CFSR寄存器的位分配表:
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="386"><p>bit31 bit16</p></td>
<td valign="top" width="155"><p>bit15 bit8</p></td>
<td valign="top" width="158"><p>bit7 bit0</p></td>
</tr>
<tr>
<td width="386"><p>用法Fault状态寄存器(<a name="OLE_LINK16" id="OLE_LINK16"></a><a name="OLE_LINK15" id="OLE_LINK15"></a>UFSR)</p></td>
<td width="155"><p>总线Fault状态寄存器(<a name="OLE_LINK8" id="OLE_LINK8"></a><a name="OLE_LINK7" id="OLE_LINK7"></a>BFSR)</p></td>
<td width="158"><p>存储器管理Fault状态寄存器(MMFSR)</p></td>
</tr>
</tbody>
</table>
SCB->CFSR寄存器可以被分成三个组:
存储器管理Fault 状态寄存器:地址0x0xE000ED28,可以按字节访问
总线Fault状态寄存器:地址0xE000ED29,可以按字节访问
用法Fault状态寄存器:地址0xE000ED2A,可以按半字访问
<strong>3.2.1.1 存储器管理Fault状态寄存器MMFSR:指示存储器访问Fault的原因</strong>
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="122"><p><br />
名称</p></td>
<td valign="top" width="525"><p>描述</p></td>
</tr>
<tr>
<td valign="top" width="50"><p><a name="_Hlk350252784" id="_Hlk350252784"></a>[7]</p></td>
<td valign="top" width="122"><p>MMARVALID</p></td>
<td valign="top" width="525"><p>存储器管理Fault地址寄存器(MMAR)有效标志:</p>
<p>0:MMAR中的值不是一个有效<a name="OLE_LINK12" id="OLE_LINK12"></a><a name="OLE_LINK11" id="OLE_LINK11"></a>Fault地址</p>
<p>1:MMAR中保留一个有效Fault地址。</p>
<p>如果发生了一个存储器管理Fault,并由于优先级的原因升级成一个硬Fault,那么硬Fault处理程序必须将该位设为0。</p></td>
</tr>
<tr>
<td valign="top" width="50"><p>[6:5]</p></td>
<td valign="top" width="122"><p>-</p></td>
<td valign="top" width="525"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="50"><p>[4]</p></td>
<td valign="top" width="122"><p>MSTKERR</p></td>
<td valign="top" width="525"><p>进入异常时的入栈操作引起的存储器管理Fault:</p>
<p>0:无入栈Fault</p>
<p>1:进入异常时的入栈操作引起了一个或一个以上的访问违犯。</p>
<p>当该位设为1时,依然要对SP进行调节,并且堆栈的上下文区域的值可能不正确。处理器没有向MMAR中写入Fault地址。</p></td>
</tr>
<tr>
<td valign="top" width="50"><p>[3]</p></td>
<td valign="top" width="122"><p>MUNSTKERR</p></td>
<td valign="top" width="525"><p>异常返回时的出栈操作引起的存储器管理Fault:</p>
<p>0:无出栈Fault</p>
<p>1:异常返回时的出栈操作已引起一个或一个以上的访问违犯.</p>
<p>该Fault与处理程序相连,这意味着当该位为1时,原始的返回堆栈仍然存在。</p>
<p>处理器不能对返回失败的SP进行调节,并且不会执行新的存储操作。处理器没有向MMAR中写入Fault地址。</p></td>
</tr>
<tr>
<td valign="top" width="50"><p>[2]</p></td>
<td valign="top" width="122"><p>-</p></td>
<td valign="top" width="525"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="50"><p>[1]</p></td>
<td valign="top" width="122"><p>DACCVIOL</p></td>
<td valign="top" width="525"><p>数据访问违犯标志:</p>
<p>0:无数据访问违犯Fault</p>
<p>1:处理器试图在不允许执行操作的位置上进行加载和存储。</p>
<p>当该位为1时,异常返回的压入堆栈的PC值指向出错指令。处理器已在MMAR中加载了目标访问的地址。</p></td>
</tr>
<tr>
<td valign="top" width="50"><p>[0]</p></td>
<td valign="top" width="122"><p>IACCVIOL</p></td>
<td valign="top" width="525"><p>指令访问违犯标志:</p>
<p>0:无指令访问违犯错误</p>
<p>1:处理器试图从不允许执行操作的位置上进行指令获取。</p>
<p>即使MPU被禁能,这一故障也会在XN(CM3内核的0xE0000000~0xFFFFFFFF区域)区寻址时发生。</p>
<p>当该位为1时,异常返回的压入堆栈的PC值指向出错指令。处理器没有向MMAR中写入故障地址。</p></td>
</tr>
</tbody>
</table>
<strong>3.2.1.2 总线Fault状态寄存器BFSR:指示总线访问Fault原因</strong>
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="53"><p>位</p></td>
<td valign="top" width="118"><p>名称</p></td>
<td valign="top" width="526"><p>描述</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[7]</p></td>
<td valign="top" width="118"><p>BFARVALID</p></td>
<td valign="top" width="526"><p>总线Fault地址寄存器(BFAR)有效标志:</p>
<p>0:BFAR中的值不是有效故障地址</p>
<p>1:BFAR中保留一个有效故障地址。</p>
<p>在地址已知的总线故障发生后处理器将该位设为1。该位可以被其他Fault清零,例如之后发生的存储器管理Fault。</p>
<p>如果发生总线Fault,并由于优先级原因升级为一个硬Fault,那么硬Fault处理程序必须将该位设为0。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[6:5]</p></td>
<td valign="top" width="118"><p>-</p></td>
<td valign="top" width="526"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[4]</p></td>
<td valign="top" width="118"><p>STKERR</p></td>
<td valign="top" width="526"><p>进入异常时的入栈操作引起的总线Fault:</p>
<p>0:无入栈故障</p>
<p>1:进入异常时的入栈操作已引起一个或一个以上的总线故障。</p>
<p>当处理器将该位设为1时,依然要对SP进行调节,并且堆栈的上下文区域的值可能不正确。处理器没有向BFAR中写入Fault地址。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[3]</p></td>
<td valign="top" width="118"><p>UNSTKERR</p></td>
<td valign="top" width="526"><p>异常返回时的出栈操作引起的总线<a name="OLE_LINK14" id="OLE_LINK14"></a><a name="OLE_LINK13" id="OLE_LINK13"></a>Fault:</p>
<p>0:无出栈Fault</p>
<p>1:异常返回时的出栈操作已引起一个或一个以上的总线Fault。</p>
<p>该Fault与处理程序相连, 这意味着当处理器将该位设为1时,原始的返回堆栈仍然存在。处理器不能对返回失败的SP进行调节,并且不会执行新的存储操作,也未向BFAR中写入Fault地址。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[2]</p></td>
<td valign="top" width="118"><p>IMPRECISERR</p></td>
<td valign="top" width="526"><p>非精确数据总线错误:</p>
<p>0:无非精确数据总线错误</p>
<p>1:已发生一个数据总线错误,但是堆栈帧中的返回地址与引起错误的指令无关。</p>
<p>当处理器将该位设为1时,不向BFAR中写入Fault地址。</p>
<p>这是一个异步Fault。因此,如果在当前进程的优先级高于总线Fault优先级时检测到该Fault,总线Fault被挂起并仅在处理器从所有更高优先级进程中返回时开始变为有效。如果在处理器进入非精确总线Fault的处理程序前发生一个精确Fault,那么处理程序同时对IMPRECISERR 和其中一个精确Fault状态位进行检测,判断它们是否置位为1。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[1]</p></td>
<td valign="top" width="118"><p>PRECISERR</p></td>
<td valign="top" width="526"><p>精确数据总线错误:</p>
<p>0:非精确数据总线错误</p>
<p>1:已发生一个数据总线错误,且异常返回的压入堆栈的PC值指向引起Fault的指令。</p>
<p>当处理器将该位设为1时,向BFAR中写入Fault地址。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[0]</p></td>
<td valign="top" width="118"><p>IBUSERR</p></td>
<td valign="top" width="526"><p>指令总线错误:</p>
<p>0:无指令总线错误</p>
<p>1:指令总线错误。</p>
<p>处理器检测到预取指令时的指令总线错误,但仅在其试图签发Fault指令时才将IBUSERR 标志设为1。</p>
<p>当处理器将该位设为1时,不向BFAR中写入Fault地址。</p></td>
</tr>
</tbody>
</table>
<strong>3.2.1.3 用法Fault状态寄存器UFSR:指示产生用法Fault的原因</strong>
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="53"><p>位</p></td>
<td valign="top" width="119"><p>名称</p></td>
<td valign="top" width="526"><p>描述</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[15:10]</p></td>
<td valign="top" width="119"><p>-</p></td>
<td valign="top" width="526"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[9]</p></td>
<td valign="top" width="119"><p>DIVBYZERO</p></td>
<td valign="top" width="526"><p>0:无除以零Fault或除以零捕获未使能</p>
<p>1:处理器已执行SDIV或UDIV指令(除以零)。</p>
<p>当处理器将该位设为1时,异常返回的压入堆栈的PC值指向执行除以零的指令。</p>
<p>注:通过将CCR中的DIV_0_TRP位设为1使能除以零捕获,默认是不使能的。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[8]</p></td>
<td valign="top" width="119"><p>UNALIGNED</p></td>
<td valign="top" width="526"><p>0:无非对齐访问Fault,或非对齐访问捕获未使能</p>
<p>1:处理器已进行了一次非对齐的存储器访问。</p>
<p>注:通过将CCR中的UNALIGN_TRP位设为1来使能非对齐访问捕获,默认是不使能的。非对齐的LDM、STM、LDRD和STRD指令总是出错,与UNALIGN_TRP的设置无关。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[7:4]</p></td>
<td valign="top" width="119"><p>-</p></td>
<td valign="top" width="526"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[3]</p></td>
<td valign="top" width="119"><p>NOCP</p></td>
<td valign="top" width="526"><p>无协处理器用法Fault。处理器不支持协处理器指令:</p>
<p>0:试图访问一个协处理器未引起用法Fault</p>
<p>1:处理器已试图访问一个协处理器。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[2]</p></td>
<td valign="top" width="119"><p>INVPC</p></td>
<td valign="top" width="526"><p>EXC_RETURN的无效PC加载引起的无效PC加载用法Fault:</p>
<p>0:没有发生无效PC加载用法Fault</p>
<p>1:处理器已试图将EXC_RETURN非法载入PC,作为一个无效的上下文或一个无效的EXC_RETURN值。</p>
<p>当该位被设为1时,异常返回的压入堆栈的PC值指向尝试执行非法PC加载的指令。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[1]</p></td>
<td valign="top" width="119"><p>INVSTATE</p></td>
<td valign="top" width="526"><p>无效状态用法Fault:</p>
<p>0:未发生无效状态用法Fault</p>
<p>1:处理器已试图执行一个非法使用EPSR的指令。</p>
<p>当该位设为1时,异常返回的压入堆栈的PC值指向一个尝试非法使用EPSR的指令。</p>
<p>如果一个未定义的指令使用了EPSR,则该位不被置位为1。</p></td>
</tr>
<tr>
<td valign="top" width="53"><p>[0]</p></td>
<td valign="top" width="119"><p>UNDEFINSTR</p></td>
<td valign="top" width="526"><p>未定义的指令用法Fault:</p>
<p>0:无未定义的指令用法Fault</p>
<p>1:处理器已试图执行一个未定义的指令。当该位设为1时,异常返回的压入堆栈的PC值指向未定义的指令。</p>
<p>未定义的指令是一条不能被处理器译码的指令。</p></td>
</tr>
</tbody>
</table>
<strong>3.2.2 SCB->HSFR寄存器</strong>
SCB->HSFR寄存器提供关于激活硬Fault处理程序的事件的信息,写入1清零相应位。
<table border="2" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="55"><p>位</p></td>
<td valign="top" width="114"><p>名称</p></td>
<td valign="top" width="528"><p>描述</p></td>
</tr>
<tr>
<td valign="top" width="55"><p>[31]</p></td>
<td valign="top" width="114"><p>DEBUGEVT</p></td>
<td valign="top" width="528"><p>硬Fault因调试事件产生,保留供调试使用。对寄存器执行写操作时,必须向该位写入0;否则,该行为不可预知。</p></td>
</tr>
<tr>
<td valign="top" width="55"><p>[30]</p></td>
<td valign="top" width="114"><p>FORCED</p></td>
<td valign="top" width="528"><p>指示硬Fault是否由上访产生,非硬Fault的处理程序无法执行时,会上访成硬Fault。</p>
<p>0:硬Fault不是因为非硬Fault上访产生的</p>
<p>1:硬Fault是通过非硬Fault上访产生的。</p>
<p>当该位设为1时,硬Fault处理程序必须读其他Fault状态寄存器以找出Fault原因。</p></td>
</tr>
<tr>
<td valign="top" width="55"><p>[29:2]</p></td>
<td valign="top" width="114"><p>-</p></td>
<td valign="top" width="528"><p>保留</p></td>
</tr>
<tr>
<td valign="top" width="55"><p>[1]</p></td>
<td valign="top" width="114"><p>VECTTBL</p></td>
<td valign="top" width="528"><p>指示一个在异常处理过程中读向量表而引起的总线Fault:</p>
<p>0:读向量表未引起总线Fault</p>
<p>1:读向量表引起了总线Fault。</p>
<p>这一错误通常情况下都由硬Fault处理程序来处理。</p></td>
</tr>
<tr>
<td valign="top" width="55"><p>[0]</p></td>
<td valign="top" width="114"><p>-</p></td>
<td valign="top" width="528"><p>保留</p></td>
</tr>
</tbody>
</table>
<strong>3.2.3 SCB->MMFAR和SCB->BFAR寄存器</strong>
为了确定产生了哪个Fault异常以及什么原因引起的Fault异常,你需要检测Fault状态寄存器。
如果SCB->CFSR寄存器的BFARVALID位有效(为1),则SCB->BFAR寄存器的值表示引起总线Fault的内存地址。
如果SCB->CFSR寄存器的MMFARVALID位有效(为1),则SCB->MMFAR寄存器的值表示引起存储器管理Fault的内存地址。