Cortex-M CPU 会在系统发生故障时引发异常。非法内存写入和读取、访问未通电的外设、执行无效指令、除以零以及其他问题都可能导致此类异常。通常在所有情况下都会引发 HardFault 异常。对于某些故障,可以启用不同的异常来专门处理这些情况。
Cortex-M 故障异常
Cortex-M 处理器实现了不同的故障异常。
HardFault 异常
HardFault 是默认异常,在任何与其他(启用的)异常无关的错误上引发。
HardFault 的优先级固定为 -1,即它的优先级高于除 NMI 之外的所有其他中断和异常。因此,当应用程序代码、中断或其他异常中发生错误时,总是可以进入 HardFault 异常处理程序。HardFault 是向量表中的异常编号 3,IRQ 编号为 -13。
内存管理异常
MemManage 异常可利用内存保护单元 (MPU) 来引发内存访问违规异常。
MemManage 是向量表中的异常编号 4,IRQ 编号 -12,并且具有可配置的优先级。
总线故障异常
任何内存访问错误都会引发 BusFault 异常。例如,非法读取、写入或向量捕获。
BusFault 是向量表中的异常编号 5,IRQ 编号为 -11,具有可配置的优先级。BusFault 可以在系统控制块 (SCB) 中明确启用。当 BusFault 未启用时,会引发 HardFault。
UsageFault 异常
执行错误时会引发 UsageFault 异常。加载/存储多个指令上的未对齐访问始终会被捕获。其他未对齐访问以及除以零的异常可在 SCB 中额外启用。
UsageFault 是向量表中的异常编号 6,IRQ 编号为 -10,具有可配置的优先级。当未启用 UsageFault 时,将改为引发 HardFault。
异常处理
发生任何异常时,都会调用从向量表读取的异常处理程序,就像中断一样。异常处理通常在开发和生产固件过程中以不同的方式进行。
开发过程中的异常处理
在固件开发过程中,可能会发生错误。开发人员通常希望分析问题所在以解决错误。
Cortex-M NVIC 提供了各种寄存器来分析崩溃的原因。此外,在大多数情况下,可以恢复崩溃发生位置的调用堆栈和寄存器内容。
调试器可以提供特殊功能来帮助分析。在这种情况下,无需向代码添加任何特殊异常处理程序。调试器只需在向量捕获或断点处中断并进行分析即可。
无需调试器的帮助,异常处理程序可以包含开发代码来收集所需信息,以便轻松地在调试器中使用它们。下面列出的 SEGGER HardFault Handler 读取相应的故障寄存器,并使其在结构中轻松可用。
生产固件中的异常处理
发布固件中也可能出现异常,应该将其捕获。
有些异常可能不是由错误引起的。例如,在未连接调试器的情况下执行 BKPT 指令会导致 HardFault。对于这些异常,固件可以简单地返回并继续执行程序。
系统可能还想从某些错误(但不是崩溃)的情况中恢复。在异常处理程序中,可以分析错误原因并从错误中恢复系统,例如通过终止导致错误的任务。
如果发生崩溃,只需重置系统即可恢复。在更高级的情况下,可以在重置之前生成崩溃转储,以便在重启时发送或保存。
异常分析
可以使用上面描述的 HardFault Handler 分析导致异常的系统状态。此外,一些调试器(例如 Ozone)提供了特殊功能来简化分析。
当 Ozone 检测到目标系统崩溃时,它会自动分析目标状态并提供所有必要的信息。异常窗口显示崩溃的原因以及发生的位置和附加 NVIC 寄存器。调用堆栈窗口还可以从异常中展开,以便轻松导航到错误的位置,即使跨多个异常也是如此。
有关更多信息,请参阅使用臭氧分析 Cortex-M 故障
故障状态寄存器
Cortex-M 系统控制块 (SCB) 包含一些寄存器,可以配置异常并提供有关故障的信息。
硬故障状态寄存器 (HFSR)
HFSR 位于地址 0xE000ED2C 的 SCB 中。它是一个 32 位寄存器。
位域:
[31] DEBUGEVT - 保留供调试器/调试探测器使用。始终写入 0。
[30] FORCED - 如果为 1,则由于禁用或优先级原因,HardFault 是由另一个异常升级引起的。
[1] VECTTBL - 如果为 1,则在读取异常处理向量表时发生 BusFault。
使用故障状态寄存器 (UFSR)
UFSR 是一个 16 位伪寄存器,是地址 0xE000ED28 处可配置故障状态寄存器 (CFSR) 的一部分。也可以通过半字访问 0xE000ED2A 直接访问它。
位域:
[9] DIVBYZERO - 如果为 1,则使用除数 0 执行 SDIV 或 UDIV 指令。
[8] UNALIGNED - 如果为 1,则在未对齐地址上执行 LDM、STM、LDRD 或 STRD,或者在启用陷阱时执行单个加载或存储。
[3] NOCP - 如果为 1,则访问不受支持的(例如不可用或未启用)协处理器。
[2] INVPC - 如果为 1,则将非法或无效的 EXC_RETURN 值加载到 PC。
[1] INVSTATE - 如果为 1,则在无效状态下执行。例如,EPSR 中的 Thumb 位未设置,或者 EPSR 中的 IT 状态无效。
[0] UNDEFINSTR - 如果为 1,则执行未定义的指令。
总线故障状态寄存器 (BFSR) 和总线故障地址寄存器 (BFAR)
BFSR 是 CFSR 中的一个 8 位伪寄存器。它可以通过从 0xE000ED29 开始的字节访问直接访问。BFAR 是一个位于 0xE000ED38 的 32 位寄存器。
位域:
[7] BFARVALID - 如果为 1,则 BFAR 包含导致 BusFault 的地址。
[5] LSPERR - 1f 1,浮点惰性堆栈保存期间发生故障。
[4] STKERR - 如果为 1,则异常入口的堆栈故障。
[3] UNSTKERR - 如果为 1,则异常返回时出栈故障。
[2] IMPRECISERR - 如果为 1,则返回地址与故障无关,例如之前导致的故障。
[1] PRECISERR - 如果为 1,则返回地址指令导致故障。
[0] IBUSERR - 如果为 1,则指令获取故障。
MemManage 故障状态寄存器 (MMFSR) 和 MemManage 故障地址寄存器 (MMFAR)
MMFSR 是 CFSR 中的一个 8 位伪寄存器。它可以通过从 0xE000ED28 开始的字节访问直接访问。MMFAR 是一个位于 0xE000ED34 的 32 位寄存器。
位域:
[7] MMARVALID - 如果为 1,则 MMFAR 包含导致 MemManageFault 的地址。
[5] MLSPERR - 1f 1,浮点惰性堆栈保存期间发生故障。
[4] MSTKERR - 如果为 1,则异常进入堆栈时发生故障。
[3] MUNSTKERR - 如果为 1,则异常返回时出栈时发生故障。
[1] DACCVIOL - 如果为 1,则数据访问违规。
[0] IACCVIOL - 如果为 1,则指令访问违规。
堆栈恢复
在异常入口处,异常处理程序可以检查发生故障时使用了哪个堆栈。当 EXC_RETURN[2] 位被设置时,使用 MSP,否则使用 PSP。
堆栈可用于恢复 CPU 寄存器的值。
CPU寄存器恢复
在异常进入时,一些 CPU 寄存器存储在堆栈中,可以从那里读取以进行错误分析。以下寄存器是可恢复的:
r0 = pStack[0]; // 寄存器 R0
r1 = pStack[1]; // 寄存器 R1
r2 = pStack[2]; // 寄存器 R2
r3 = pStack[3]; // 寄存器 R3
r12 = pStack[4]; // 寄存器 R12
lr = pStack[5]; // 链接寄存器 LR
pc = pStack[6]; // 程序计数器 PC
psr.byte = pStack[7]; // 程序状态字 PSR
故障分析示例
以下示例显示了某些故障是如何/为何发生的,以及如何分析它们。此处提供了一个用于测试这些故障的项目。
BusFault 示例
非法内存写入
/****************************************************************************
*
* _IllegalWrite()
*
* 函数说明
* 通过写入保留地址来触发 BusFault 或 HardFault。
*
* 附加信息
* 写入指令后执行某些指令会引发 BusFault。
* 故障时相关寄存器:
* HFSR = 0x40000000
* FORCED = 1 - BusFault 升级为 HardFault(当 BusFault 未激活时)
* BFSR = 0x00000004
* IMPRECISERR = 1 - 不精确的数据访问违规。返回地址与故障无关
* BFARVALID = 0 - BFAR 无效
*/
static int _IllegalWrite ( void ) {
int r ;
volatile unsigned int * p ;
r = 0 ;
p = ( unsigned int * ) 0x00100000 ; // 0x00100000-0x07FFFFFF 在 STM32F4 上保留
// F44F1380 mov.w r3, #0x00100000
* p = 0x00BADA55 ;
// 4A03 ldr r2, =0x00BADA55
// 601A str r2, [r3] <- 此处执行了非法写入
return r ;
// 9B00 ldr r3, [sp]
// 4618 mov r0, r3
// B002 add sp, sp, #8 <- 此处可能会引发故障
// 4770 bx lr
}
非法内存读取
/****************************************************************************
*
* _IllegalRead()
*
* 功能说明
* 通过从保留地址读取来触发 BusFault 或 HardFault。
*
* 附加信息
* 读取指令后立即触发 BusFault。
* 故障时相关寄存器:
* HFSR = 0x40000000
* FORCED = 1 - BusFault 升级为 HardFault
* BFSR = 0x00000082
* PRECISERR = 1 - 精确数据访问违规
* BFARVALID = 1 - BFAR 有效
* BFAR = 0x00100000 - 读取的地址
*/
static int _IllegalRead ( void ) {
int r ;
volatile unsigned int * p ;
p = ( unsigned int * ) 0x00100000 ; // 0x00100000-0x07FFFFFF 在 STM32F4 上保留
// F44F1380 mov.w r3, #0x00100000 <- 读取地址。将在 BFAR 中找到
r = * p ;
// 681B ldr r3, [r3] <- 此处发生非法读取并引发 BusFault
// 9300 str r3, [sp]
return r ;
}
非法函数执行
/****************************************************************************
*
* _IllegalFunc()
*
* 功能说明
* 通过在保留地址执行来触发 BusFault 或 HardFault。
*
* 附加信息
* 在无效地址执行时触发 BusFault。
* 故障时相关寄存器:
* HFSR = 0x40000000
* FORCED = 1 - BusFault 升级为 HardFault
* BFSR = 0x00000001
* IBUSERR = 1 - 指令预取时发生 BusFault
*/
static int _IllegalFunc ( void ) {
int r ;
int ( * pF )( void );
pF = ( int ( * )( void )) 0x00100001 ; // 0x00100000-0x07FFFFFF 在 STM32F4 上保留
// F44F1380 mov.w r3, #0x00100001
r = pF (); // 4798 blx r3 <- 分支到非法地址,导致从 0x00100000 获取并返回故障异常
return r ;
}
UsageFault 示例
未定义指令执行
/****************************************************************************
*
* _UndefInst()
*
* 函数说明
* 执行未定义的指令时触发 UsageFault 或 HardFault。
*
* 附加信息
* 在无效地址执行时触发 UsageFault。
* 硬故障的相关寄存器:
* HFSR = 0x40000000
* FORCED = 1 - UsageFault 升级为 HardFault
* UFSR = 0x0001
* UNDEFINSTR = 1 - 执行了未定义的指令
*/
static int _UndefInst ( void ) {
static const unsigned short _UDF [ 4 ] = { 0xDEAD , 0xDEAD , 0xDEAD , 0xDEAD }; // 0xDEAD: UDF #<imm> (永久未定义)
int r ;
int ( * pF )( void );
pF = ( int ( * )( void ))((( char * ) & _UDF ) + 1 );
// 4B05 ldr r3, =0x08001C18 <_UDF> <- 加载“RAM Code”指令的地址
// 3301 添加 r3, #1 <- 确保 Thumb 位已设置
r = pF ();
// 4798 blx r3 <- 调用“RAM Code”,将执行 UDF 指令并引发异常
// 9000 str r0, [sp]
return r ;
}
非法状态
/****************************************************************************
*
* _NoThumbFunc()
*
* 功能说明
* 执行未设置 thumb 位的地址时触发 UsageFault 或 HardFault。
*
* 附加信息
* 在无效地址执行时触发 UsageFault。
* 硬故障的相关寄存器:
* HFSR = 0x40000000
* FORCED = 1 - UsageFault 升级为 HardFault
* UFSR = 0x0002
* INVSTATE = 1 - 以无效状态执行指令
*/
static int _NoThumbFunc ( void ) {
int r ;
int ( * pF )( void );
pF = ( int ( * )( void )) 0x00100000 ; // 0x00100000-0x07FFFFFF 在 STM32F4 上保留
// F44F1380 mov.w r3, #0x00100000 <- 请注意,位 [0] 未设置。
r = pF ();
// 4798 blx r3 <- 分支交换,模式更改为 ARM,但 Cortex-M 仅支持 Thumb 模式。
return r ;
}
被零除
/****************************************************************************
*
* _DivideByZero()
*
* 函数说明
* 通过除以零触发 UsageFault 或 HardFault。
*
* 附加信息
* 在除法指令上立即触发 UsageFault。
* 硬故障上的相关寄存器:
* HFSR = 0x40000000
* FORCED = 1 - UsageFault 升级为 HardFault
* UFSR = 0x0200
* DIVBYZERO = 1 - 除以零故障
*/
static int _DivideByZero ( void ) {
int r ;
volatile unsigned int a ;
volatile unsigned int b ;
a = 1 ;
// 2301 movs r3, #1 <- Load divided into a decimal error
b = 0 ;
// 2300 movs r3, #0 <- 加载除数
r = a / b ;
// FBB2F3F3 udiv r3, r2, r3 <- 除以 0 引发故障异常
return r ;
}
未对齐访问
/****************************************************************************
*
* _UnalignedAccess()
*
* 函数说明
* 通过未对齐的字访问触发 UsageFault 或 HardFault。
*
* 附加信息
* 在读取或写入指令时立即触发 UsageFault。
* 故障时相关寄存器:
* HFSR = 0x40000000
* FORCED = 1 - UsageFault 升级为 HardFault
* UFSR = 0x0100
* UNALIGNED = 1 - 未对齐的内存访问
*/
static int _UnalignedAccess ( void ) {
int r ;
volatile unsigned int * p ;
p = ( unsigned int * ) 0x20000002 ;
// 4B04 ldr r3, =0x20000002 <- 未字对齐的地址
r = * p ;
// 681B ldr r3, [r3] <- 从未对齐的地址加载字会引发异常
// 9300 str r3, [sp]
return r ;
}
HardFault 示例
非法向量表获取
/****************************************************************************
*
* _IllegalVector()
*
* 函数说明
* 使用非法向量表通过中断触发 HardFault。
*
* 附加信息
* 故障时相关寄存器:
* HFSR = 0x00000002
* VECTTBL = 1 - 向量表读取故障
*/
static int _IllegalVector ( void ) {
int r ;
SCB -> VTOR = 0x001000000 ; // 将向量表重定位到非法地址
// 4B09 ldr r3, =0xE000ED00
// F04F7280 mov.w r2, #0x1000000
// 609A str r2, [r3, #8]
SCB -> ICSR = SCB_ICSR_PENDSVSET_Msk ; // 触发 PendSV 异常以读取无效向量
// 4B07 ldr r3, =0xE000ED00
// F04F5280 mov.w r2, #0x10000000
// 605A str r2, [r3, #4]
__ISB ();
// F3BF8F6F isb <- 将执行 PendSV 异常。尝试从非法地址 0x00100038 读取 PendSV 向量导致故障异常
// BF00 nop
__DSB ();
// F3BF8F4F dsb sy
// BF00 nop
return r ;
}
文件列表
SEGGER 硬故障处理程序
;/*********************************************************************
;*
;* HardFault_Handler()
;*
;* Function description
;* Evaluates the used stack (MSP, PSP) and passes the appropiate
;* stack pointer to the HardFaultHandler "C"-routine.
;*
;* Notes
;* (1) Ensure that HardFault_Handler is part of the exception table
;*/
HardFault_Handler:
BusFault_Handler:
UsageFault_Handler:
MemManage_Handler:
#if (defined (__IAR_SYSTEMS_ASM__) && (__ARM6M__) && (__CORE__ == __ARM6M__)) || \
(defined(__CC_ARM) || (defined __clang__)) && (__TARGET_ARCH_6S_M) || \
(defined (__GNUC__) && ((__ARM_ARCH_6M__) || (__ARM_ARCH_8M_BASE__)))
;// This version is for Cortex M0
movs R0, #4
mov R1, LR
tst R0, R1 ;// Check EXC_RETURN in Link register bit 2.
bne Uses_PSP
mrs R0, MSP ;// Stacking was using MSP.
b Pass_StackPtr
Uses_PSP:
mrs R0, PSP ;// Stacking was using PSP.
Pass_StackPtr:
ldr R2,=HardFaultHandler
bx R2 ;// Stack pointer passed through R0.
#else
;// This version is for Cortex M3, Cortex M4 and Cortex M4F
tst LR, #4 ;// Check EXC_RETURN in Link register bit 2.
ite EQ
mrseq R0, MSP ;// Stacking was using MSP.
mrsne R0, PSP ;// Stacking was using PSP.
b HardFaultHandler ;// Stack pointer passed through R0.
#endif
.end
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 2014 - 2023 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* conditions are met: *
* *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* - Neither the name of SEGGER Microcontroller GmbH *
* nor the names of its contributors may be used to endorse or *
* promote products derived from this software without specific *
* prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. *
* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
File : SEGGER_HardFaultHandler.c
Purpose : Generic SEGGER HardFault handler for Cortex-M
Literature:
[1] Analyzing HardFaults on Cortex-M CPUs (https://www.segger.com/downloads/appnotes/AN00016_AnalyzingHardFaultsOnCortexM.pdf)
Additional information:
This HardFault handler enables user-friendly analysis of hard faults
in debug configurations.
If a release configuration requires a HardFault handler,
a specific HardFault handler should be included instead,
which for example issues a reset or turns on an error LED.
-------- END-OF-HEADER ---------------------------------------------
*/
/*********************************************************************
*
* Defines
*
**********************************************************************
*/
#define SCB_SHCSR (*(volatile unsigned int*) (0xE000ED24u)) // System Handler Control and State Register
#define SCB_MMFSR (*(volatile unsigned char*) (0xE000ED28u)) // MemManage Fault Status Register
#define SCB_BFSR (*(volatile unsigned char*) (0xE000ED29u)) // Bus Fault Status Register
#define SCB_UFSR (*(volatile unsigned short*)(0xE000ED2Au)) // Usage Fault Status Register
#define SCB_HFSR (*(volatile unsigned int*) (0xE000ED2Cu)) // Hard Fault Status Register
#define SCB_DFSR (*(volatile unsigned int*) (0xE000ED30u)) // Debug Fault Status Register
#define SCB_MMFAR (*(volatile unsigned int*) (0xE000ED34u)) // MemManage Fault Manage Address Register
#define SCB_BFAR (*(volatile unsigned int*) (0xE000ED38u)) // Bus Fault Address Register
#define SCB_AFSR (*(volatile unsigned int*) (0xE000ED3Cu)) // Auxiliary Fault Status Register
#ifndef DEBUG // Should be overwritten by project settings
#define DEBUG (0) // in debug builds
#endif
/*********************************************************************
*
* Prototypes
*
**********************************************************************
*/
#ifdef __cplusplus
extern "C" {
#endif
void HardFaultHandler(unsigned int* pStack);
#ifdef __cplusplus
}
#endif
/*********************************************************************
*
* Static data
*
**********************************************************************
*/
#if DEBUG
static volatile unsigned int _Continue; // Set this variable to 1 to run further
static struct {
struct {
volatile unsigned int r0; // Register R0
volatile unsigned int r1; // Register R1
volatile unsigned int r2; // Register R2
volatile unsigned int r3; // Register R3
volatile unsigned int r12; // Register R12
volatile unsigned int lr; // Link register
volatile unsigned int pc; // Program counter
union {
volatile unsigned int word;
struct {
unsigned int IPSR : 8; // Interrupt Program Status register (IPSR)
unsigned int EPSR : 19; // Execution Program Status register (EPSR)
unsigned int APSR : 5; // Application Program Status register (APSR)
} bits;
} psr; // Program status register.
} SavedRegs;
union {
volatile unsigned int word;
struct {
unsigned int MEMFAULTACT : 1; // [0] Read as 1 if memory management fault is active
unsigned int BUSFAULTACT : 1; // [1] Read as 1 if bus fault exception is active
unsigned int HARDFAULTACT : 1; // [2] Read as 1 if hard fault exception is active (ARMv8-M)
unsigned int USGFAULTACT : 1; // [3] Read as 1 if usage fault exception is active
unsigned int SECUREFAULTACT : 1; // [4] Read as 1 if secure fault exception is active (ARMv8-M)
unsigned int NMIACT : 1; // [5] Read as 1 if NMI exception is active (ARMv8-M)
unsigned int : 1;
unsigned int SVCALLACT : 1; // [7] Read as 1 if SVC exception is active
unsigned int MONITORACT : 1; // [8] Read as 1 if debug monitor exception is active
unsigned int : 1;
unsigned int PENDSVACT : 1; // [10] Read as 1 if PendSV exception is active
unsigned int SYSTICKACT : 1; // [11] Read as 1 if SYSTICK exception is active
unsigned int USGFAULTPENDED : 1; // [12] Usage fault pending; higher priority exception active
unsigned int MEMFAULTPENDED : 1; // [13] Memory management fault pending; higher priority exception active
unsigned int BUSFAULTPENDED : 1; // [14] Bus fault pending; higher priority exception active
unsigned int SVCALLPENDED : 1; // [15] SVC pending; higher priority exception active
unsigned int MEMFAULTENA : 1; // [16] Memory management fault exception enable
unsigned int BUSFAULTENA : 1; // [17] Bus fault exception enable
unsigned int USGFAULTENA : 1; // [18] Usage fault exception enable
unsigned int SECUREFAULTENA : 1; // [19] Secure fault exception enable (ARMv8-M)
unsigned int SECUREFAULTPENDED : 1; // [20] Secure fault exception pending; higher priority exception active (ARMv8-M)
unsigned int HARDFAULTPENDED : 1; // [21] Hard fault exception pending (ARMv8-M)
unsigned int : 10;
} bits;
} shcsr; // System Handler Control and State Register (0xE000ED24)
union {
volatile unsigned char byte;
struct {
unsigned int IACCVIOL : 1; // [0] Instruction access violation
unsigned int DACCVIOL : 1; // [1] Data access violation
unsigned int : 1;
unsigned int MUNSTKERR : 1; // [3] Unstacking error
unsigned int MSTKERR : 1; // [4] Stacking error
unsigned int MLSPERR : 1; // [5] MemManage fault during FP lazy state preservation
unsigned int : 1;
unsigned int MMARVALID : 1; // [7] Indicates the MMAR is valid
unsigned int : 24;
} bits;
} mmfsr; // MemManage Fault Status Register (0xE000ED28)
volatile unsigned int mmfar; // MemManage Fault Address Register (0xE000ED34)
union {
volatile unsigned char byte;
struct {
unsigned int IBUSERR : 1; // [0] Instruction access violation
unsigned int PRECISERR : 1; // [1] Precise data access violation
unsigned int IMPRECISERR : 1; // [2] Imprecise data access violation
unsigned int UNSTKERR : 1; // [3] Unstacking error
unsigned int STKERR : 1; // [4] Stacking error
unsigned int LSPERR : 1; // [5] Bus fault during FP lazy state preservation
unsigned int : 1;
unsigned int BFARVALID : 1; // [7] Indicates BFAR is valid
unsigned int : 24;
} bits;
} bfsr; // Bus Fault Status Register (0xE000ED29)
volatile unsigned int bfar; // Bus Fault Address Register (0xE000ED38)
union {
volatile unsigned short halfword;
struct {
unsigned int UNDEFINSTR : 1; // [0] Attempts to execute an undefined instruction
unsigned int INVSTATE : 1; // [1] Attempts to switch to an invalid state (e.g., ARM)
unsigned int INVPC : 1; // [2] Attempts to do an exception with a bad value in the EXC_RETURN number
unsigned int NOCP : 1; // [3] Attempts to execute a coprocessor instruction
unsigned int STKOF : 1; // [4] Indicates whether a stack overflow error has occurred (ARMv8-M)
unsigned int : 3;
unsigned int UNALIGNED : 1; // [8] Indicates that an unaligned access fault has taken place
unsigned int DIVBYZERO : 1; // [9] Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)
unsigned int : 22;
} bits;
} ufsr; // Usage Fault Status Register (0xE000ED2A)
union {
volatile unsigned int word;
struct {
unsigned int : 1;
unsigned int VECTTBL : 1; // [1] Indicates hard fault is caused by failed vector fetch
unsigned int : 28;
unsigned int FORCED : 1; // [30] Indicates hard fault is taken because of bus fault/memory management fault/usage fault
unsigned int DEBUGEVT : 1; // [31] Indicates hard fault is triggered by debug event
} bits;
} hfsr; // Hard Fault Status Register (0xE000ED2C)
union {
volatile unsigned int word;
struct {
unsigned int HALTED : 1; // [0] Halt requested in NVIC
unsigned int BKPT : 1; // [1] BKPT instruction executed
unsigned int DWTTRAP : 1; // [2] DWT match occurred
unsigned int VCATCH : 1; // [3] Vector fetch occurred
unsigned int EXTERNAL : 1; // [4] EDBGRQ signal asserted
unsigned int PMU : 1; // [5] PMU counter overflow event has occurred
unsigned int : 26;
} bits;
} dfsr; // Debug Fault Status Register (0xE000ED30)
volatile unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)
} HardFaultRegs;
#endif
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* HardFaultHandler()
*
* Function description
* C part of the hard fault handler which is called by the assembler
* function HardFault_Handler
*/
void HardFaultHandler(unsigned int* pStack) {
//
// In case we received a hard fault because of a breakpoint instruction, we return.
// This may happen when using semihosting for printf outputs and no debugger is connected,
// i.e. when running a "Debug" configuration in release mode.
//
if (SCB_HFSR & (1u << 31)) {
SCB_HFSR |= (1u << 31); // Reset Hard Fault status
*(pStack + 6u) += 2u; // PC is located on stack at SP + 24 bytes. Increment PC by 2 to skip break instruction.
return; // Return to interrupted application
}
#if DEBUG
//
// Read NVIC registers
//
HardFaultRegs.shcsr.word = SCB_SHCSR; // System Handler Control and State Register
HardFaultRegs.mmfsr.byte = SCB_MMFSR; // MemManage Fault Status Register
HardFaultRegs.mmfar = SCB_MMFAR; // MemManage Fault Address Register
HardFaultRegs.bfsr.byte = SCB_BFSR; // Bus Fault Status Register
HardFaultRegs.bfar = SCB_BFAR; // Bus Fault Manage Address Register
HardFaultRegs.ufsr.halfword = SCB_UFSR; // Usage Fault Status Register
HardFaultRegs.hfsr.word = SCB_HFSR; // Hard Fault Status Register
HardFaultRegs.dfsr.word = SCB_DFSR; // Debug Fault Status Register
HardFaultRegs.afsr = SCB_AFSR; // Auxiliary Fault Status Register
//
// Halt execution
// If NVIC registers indicate readable memory, change the variable value to != 0 to continue execution.
//
_Continue = 0u;
while (_Continue == 0u) {
}
//
// Read saved registers from the stack.
//
HardFaultRegs.SavedRegs.r0 = pStack[0]; // Register R0
HardFaultRegs.SavedRegs.r1 = pStack[1]; // Register R1
HardFaultRegs.SavedRegs.r2 = pStack[2]; // Register R2
HardFaultRegs.SavedRegs.r3 = pStack[3]; // Register R3
HardFaultRegs.SavedRegs.r12 = pStack[4]; // Register R12
HardFaultRegs.SavedRegs.lr = pStack[5]; // Link register LR
HardFaultRegs.SavedRegs.pc = pStack[6]; // Program counter PC
HardFaultRegs.SavedRegs.psr.word = pStack[7]; // Program status word PSR
//
// Halt execution
// To step out of the HardFaultHandler, change the variable value to != 0.
//
_Continue = 0u;
while (_Continue == 0u) {
}
#else
//
// If this module is included in a release configuration, simply stay in the HardFault handler
//
(void)pStack;
do {
} while (1);
#endif
}
/*************************** End of file ****************************/
Exception Test Application
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 2014 - 2020 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* conditions are met: *
* *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* - Neither the name of SEGGER Microcontroller GmbH *
* nor the names of its contributors may be used to endorse or *
* promote products derived from this software without specific *
* prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. *
* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
Purpose : Cortex-M Fault Exception test application.
*/
#include <stdio.h>
#include <stdlib.h>
#include "STM32F4xx.h"
#define _USE_DISTINCT_FAULTS // If defined, enable usage fault, bus fault, and mem manage fault
#define _NICE_CALL_STACK // If defined, generate a "useful" call stack to test stack unwinding
/*********************************************************************
*
* Local functions, generate fault with nice call stack
*
**********************************************************************
*/
#ifdef _NICE_CALL_STACK
#include <string.h>
static const char _abLogo[128*32];
static const char _abIcon[32*32];
#define _NUM_IMAGES 2
static struct {
const char* sName;
int Width;
int Height;
const char* pData;
} _aImg[_NUM_IMAGES] = {
{"Logo.bmp", 128, 32, _abLogo},
{"Icon.bmp", 32, 32, _abIcon}
};
/*********************************************************************
*
* _GetPixel()
*
* Function description
* Get one pixel within an image.
*/
static char _GetPixel(const char* pImg, int x, int y, int Width, int Height) {
return *(pImg + (y * Width) + x);
}
/*********************************************************************
*
* _GetImage()
*
* Function description
* Find the given image and get its informaiton.
*
* Return value
* == NULL: Image not found. Width and height not valid.
* != NULL: Pointer to the image data.
*/
static const char* _GetImage(const char* sName, int* pWidth, int* pHeight) {
int i;
for (i = 0; i < _NUM_IMAGES; i++) {
if (strcmp(sName, _aImg[i].sName) == 0) {
*pWidth = _aImg[i].Width;
*pHeight = _aImg[i].Height;
return _aImg[i].pData;
}
}
return NULL;
}
/*********************************************************************
*
* _GetFrameBuf()
*
* Function description
* Get the frame buffer and its dimensions.
*/
static char* _GetFrameBuf(int* pWidth, int* pHeight) {
*pWidth = 1920;
*pHeight = 1080;
return (char*)0x20000000;
}
/*********************************************************************
*
* _RenderImage()
*
* Function description
* Render an image to be written to the display.
*/
static void _RenderImage(void) {
const char* pImg;
char* pFrameBuf;
char Pixel;
unsigned Width;
unsigned Height;
unsigned x;
unsigned y;
pFrameBuf = _GetFrameBuf(&Width, &Height);
pImg = _GetImage("logo.bmp", &Width, &Height);
for (y = 0; y < Height; y++) {
for (x = 0; x < Width; x++) {
Pixel = _GetPixel(pImg, x, y, Width, Height);
*pFrameBuf = Pixel;
}
}
}
#endif
/*********************************************************************
*
* Local functions, Bus faults
*
**********************************************************************
*/
/*********************************************************************
*
* _IllegalRead()
*
* Function description
* Trigger a bus fault or hard fault by reading from a reserved address.
*
* Additional Information
* Bus fault is immediately triggered on the read instruction.
* Related NVIC registers on hard fault:
* NVIC.HFSR = 0x40000000
* FORCED = 1 - bus fault/memory management fault/usage fault escalated to hard fault
* NVIC.BFSR = 0x00000082
* PRECISERR = 1 - Precise data access violation
* BFARVALID = 1 - BFAR is valid
* NVIC.BFAR = 0x00100000 - The address read from
*/
static int _IllegalRead(void) {
int r;
volatile unsigned int* p;
p = (unsigned int*)0x00100000; // 0x00100000-0x07FFFFFF is reserved on STM32F4
r = *p;
return r;
}
/*********************************************************************
*
* _IllegalWrite()
*
* Function description
* Trigger a bus fault or hard fault by writing to a reserved address.
*
* Additional Information
* Bus fault is triggered some instructions after the write instruction.
* Related NVIC registers on hard fault:
* NVIC.HFSR = 0x40000000
* FORCED = 1 - bus fault/memory management fault/usage fault escalated to hard fault
* NVIC.BFSR = 0x00000004
* IMPRECISERR = 1 - Imprecise data access violation
* BFARVALID = 0 - BFAR not valid
*/
static int _IllegalWrite(void) {
int r;
volatile unsigned int* p;
p = (unsigned int*)0x00100000; // 0x00100000-0x07FFFFFF is reserved on STM32F4
*p = 0x00BADA55;
return r;
}
/*********************************************************************
*
* _IllegalFunc()
*
* Function description
* Trigger a bus fault or hard fault by executing at a reserved address.
*
* Additional Information
* Bus fault is triggered on execution at the invalid address.
* Related NVIC registers on hard fault:
* NVIC.HFSR = 0x40000000
* FORCED = 1 - bus fault/memory management fault/usage fault escalated to hard fault
* NVIC.BFSR = 0x00000001
* IBUSERR = 1 - Bus fault on instruction prefetch
*/
static int _IllegalFunc(void) {
int r;
int (*pF)(void);
pF = (int(*)(void))0x00100000; // 0x00100000-0x07FFFFFF is reserved on STM32F4
r = pF();
return r;
}
/*********************************************************************
*
* Local functions, Usage Faults
*
**********************************************************************
*/
/*********************************************************************
*
* _NoThumbFunc()
*
* Function description
* Trigger a usage fault or hard fault by executing an address without thumb bit set.
*
* Additional Information
* Usage fault is triggered on execution at the invalid address.
* Related NVIC registers on hard fault:
* NVIC.HFSR = 0x40000000
* FORCED = 1 - bus fault/memory management fault/usage fault escalated to hard fault
* NVIC.UFSR = 0x0002
* INVSTATE = 1 - Instruction execution with invalid state
*/
static int _NoThumbFunc(void) {
int r;
int (*pF)(void);
pF = (int(*)(void))0x00000000;
r = pF();
return r;
}
/*********************************************************************
*
* _UndefInst()
*
* Function description
* Trigger a usage fault or hard fault by executing an undefined instruction.
*
* Additional Information
* Usage fault is triggered on execution at the invalid address.
* Related NVIC registers on hard fault:
* NVIC.HFSR = 0x40000000
* FORCED = 1 - bus fault/memory management fault/usage fault escalated to hard fault
* NVIC.UFSR = 0x0001
* UNDEFINSTR = 1 - Undefined instruction executed
*/
static int _UndefInst(void) {
static const unsigned short _UDF[4] = {0xDEAD, 0xDEAD, 0xDEAD, 0xDEAD};
int r;
int (*pF)(void);
pF = (int(*)(void))(((char*)&_UDF) + 1);
r = pF();
return r;
}
/*********************************************************************
*
* _UnalignedAccess()
*
* Function description
* Trigger a usage fault or hard fault by an unaligned word access.
*
* Additional Information
* Usage fault is triggered immediately on the read or write instruction.
* Related NVIC registers on hard fault:
* NVIC.HFSR = 0x40000000
* FORCED = 1 - bus fault/memory management fault/usage fault escalated to hard fault
* NVIC.UFSR = 0x0100
* UNALIGNED = 1 - Unaligned memory access
*/
static int _UnalignedAccess(void) {
int r;
volatile unsigned int* p;
p = (unsigned int*)0x20000002; // 0x00100000-0x07FFFFFF is reserved on STM32F4
r = *p;
return r;
}
/*********************************************************************
*
* _DivideByZero()
*
* Function description
* Trigger a usage fault or hard fault by dividing by zero.
*
* Additional Information
* Usage fault is triggered immediately on the divide instruction.
* Related NVIC registers on hard fault:
* NVIC.HFSR = 0x40000000
* FORCED = 1 - bus fault/memory management fault/usage fault escalated to hard fault
* NVIC.UFSR = 0x0200
* DIVBYZERO = 1 - Divide-by-zero fault
*/
static int _DivideByZero(void) {
int r;
volatile unsigned int a;
volatile unsigned int b;
a = 1;
b = 0;
r = a / b;
return r;
}
/*********************************************************************
*
* Local functions, Hard faults
*
**********************************************************************
*/
/*********************************************************************
*
* _IllegalVector()
*
* Function description
* Trigger a hard fault by interrupt with illegal vector table.
*
* Additional Information
* Related NVIC registers on hard fault:
* NVIC.HFSR = 0x00000002
* VECTTBL = 1 - Vector table read fault
*/
static int _IllegalVector(void) {
int r;
SCB->VTOR = 0x001000000; // Relocate vector table to illegal address
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; // Trigger PendSV exception to read invalid vector
__ISB();
__DSB();
return r;
}
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* main()
*
* Function description
* Application entry point.
*/
#include <time.h>
void main(void) {
int r;
//
// Enable fault on divide-by-zero and unaligned access
//
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk
| SCB_CCR_UNALIGN_TRP_Msk;
#ifdef _USE_DISTINCT_FAULTS
//
// Enable usage fault, bus fault, and mem manage fault
//
SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk
| SCB_SHCSR_BUSFAULTENA_Msk
| SCB_SHCSR_MEMFAULTENA_Msk; // enable Usage-/Bus-/MPU Fault
#endif
#ifdef _NICE_CALL_STACK // Generate a "useful" call stack to test stack unwinding
_RenderImage();
#endif
//
// Un-comment one of the following function calls to test the corresponding fault
//
//r = _UndefInst();
//r = _NoThumbFunc();
//r = _IllegalFunc();
//r = _UnalignedAccess();
//r = _DivideByZero();
//r = _IllegalWrite();
//r = _IllegalRead();
//r = _IllegalVector();
do {
r++;
} while (1);
}
/*************************** End of file ****************************/