11

潮声隔雨深 · 2020年09月10日

Cortex-M3和Cortex-M4 Fault异常应用之一 ----- 基础知识

转自:CSDN
作者:zhzht19861011

1. 摘要

Cortex-M内核实现了一个高效异常处理模块,可以捕获非法内存访问和数个程序错误条件。本应用笔记从程序员角度描述Cortex-M Fault异常,并且讲述在软件开发周期中的Fault用法。

2. 简介

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异常或者提供引发异常的原因信息。

更深入的文档

完整的异常描述见《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权威指南》)。

3. Cortex-M Fault异常和寄存器

每个符合CMSIS规范的编译器所提供的启动文件(Startup_device)都会定义好设备所有的异常和中断向量。这些向量表定义了异常或中断处理程序的入口地址。下表给出了一个典型的向量表,Fault异常向量用蓝色标注。

   1:      : 
   3:      : 
   5:  __Vectors DCD __initial_sp ; 栈顶
   7:  DCD Reset_Handler          ; 复位处理程序入口
   9:  DCD NMI_Handler            ; NMI 处理程序入口
  11:  DCD HardFaul t_Handler     ; 硬Fault处理程序入口 
  13:  DCD MemManage_Handler      ; 存储器管理处理程序入口 
  15:  DCD BusFault_Handler       ; 总线Fault 处理程序入口 
  17:  DCD UsageFault_Handler     ; 用法 Fault 处理程序入口 
  19:  DCD 0                      ; 保留 
  21:      : 
  23:      : 

通常总是使能硬Fault异常的,硬Fault异常具有固定的优先级,并且优先级高于其它Fault异常以及中断,但低于NMI。硬Fault异常处理程序在以下情况下会被执行:其它非硬Fault异常(非硬Fault异常是指总线、存储器管理和用法Fault 异常,下同。)被禁能,并且这些Fault异常被触发;在执行一个非硬Fault异常处理程序中又产生非硬Fault异常。

所有非硬Fault具有可编程的优先级。当Cortex-M内核复位后,这些非硬Fault被禁能,你可以在应用软件中通过设置“系统Handler控制及状态寄存器(SHCSR)”来使能非硬Fault异常。这个寄存器属于系统控制模寄存器组(SCB)

3.1 Fault异常的控制寄存器

在这里有必要介绍一下系统控制模块寄存器组(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; 
  1. 定义系统控制寄存器组物理空间基地址
#define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */
  1. 定义指向系统控制寄存器组的指针
#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异常控制寄存器列表:

image.png

3.1.1 SCB->CCR 寄存器

蓝色部分控制是否使能相应的用法Fault

image.png

3.1.2 SCB->SHP 寄存器组

以下SCB->SHP 寄存器组的寄存器用来设置异常处理程序的优先级:

  • SCB->SHP[0]:存储器管理Fault的优先级
  • SCB->SHP[1]:总线Fault的优先级
  • SCB->SHP[2]:用法Fault的优先级

为了编程中断和异常的优先级,CMSIS提供了函数NVIC_SetPrioriity和NVIC_GetPriority。这两个函数也位于core_cm3.h中,源码为:

/** \brief Set Interrupt Priority 
     
     This function sets the priority for the specified interrupt. The interrupt number can be positive 
       to specify an external (device specific) interrupt, or negative to specify an internal (core)
       interrupt. 
     Note: The priority cannot be set for every core interrupt. 
     
     \param [in] IRQn Number of the interrupt for set priority 
     \param [in] priority Priority to set 
     */ 
     static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) 
    { 
        if(IRQn < 0) { 
                /* set Priority for Cortex-M System Interrupts */ 
            SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); }
        else { 
                  /* set Priority for device specific Interrupts */ 
            NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); }
    } 
     
    /** \brief Get Interrupt Priority 
    
    This function reads the priority for the specified interrupt. The interrupt number can be positive
       to specify an external (device specific) interrupt, or negative to specify an internal (core) 
       interrupt. 
    The returned priority value is automatically aligned to the implemented priority bits of the 
       microcontroller. 
    
    \param [in] IRQn Number of the interrupt for get priority 
    \return Interrupt Priority 
    */ 
    static __INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) 
    { 
        if(IRQn < 0) { 
                   /* get priority for Cortex-M system interrupts */ 
            return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); }
        else { 
                   /* get priority for device specific interrupts */ 
            return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); }
    } 

可以通过下面的示例代码更改异常优先级:

             : 
             : 
         NVIC_SetPriority (MemoryManagement_IRQn, 0xF0); 
         NVIC_SetPri ority (BusFault_IRQn, 0x80); 
         NVIC_SetPriority ( UsageFault_IRQn, 0x10); 
             : 
         UsageFault_prio = NVIC_GetPriority ( UsageFault_IRQn); 
             : 
             : 

3.1.3 SCB->SHCSR寄存器

与Fault异常相关位见下表的蓝色部分

image.png

尽管可以写SCB->SHCSR寄存器的所有位,但建议软件只写异常使能位。下面的例子用于使能所有非硬Fault(存储器管理Fault、总线Fault、用法Fault异常):

SCB - >SHCSR |= 0x00007000; /*enable Usage Fault, Bus Fault, and MMU Fault*/ 

注:要包含core_cm3.h头文件。

3.2 Fault异常的状态和地址寄存器

Fault状态寄存器组(SCB->CFSR和SCB->HFSR)和Fault地址寄存器组(SCB->MMAR和SCB->BFAR)包含Fault的详细信息以及异常发生时访问的内存地址。

image.png

3.2.1 SCB->CFSR寄存器

SCB->CFSR寄存器的位分配表:

image.png

SCB->CFSR寄存器可以被分成三个组:

  • 存储器管理Fault 状态寄存器:地址0x0xE000ED28,可以按字节访问
  • 总线Fault状态寄存器:地址0xE000ED29,可以按字节访问
  • 用法Fault状态寄存器:地址0xE000ED2A,可以按半字访问

3.2.1.1 存储器管理Fault状态寄存器MMFSR:指示存储器访问Fault的原因

image.png

3.2.1.2 总线Fault状态寄存器BFSR:指示总线访问Fault原因

image.png

3.2.1.3 用法Fault状态寄存器UFSR:指示产生用法Fault的原因
image.png

3.2.2 SCB->HSFR寄存器

SCB->HSFR寄存器提供关于激活硬Fault处理程序的事件的信息,写入1清零相应位。

image.png

3.2.3 SCB->MMFAR和SCB->BFAR寄存器

为了确定产生了哪个Fault异常以及什么原因引起的Fault异常,你需要检测Fault状态寄存器。

如果SCB->CFSR寄存器的BFARVALID位有效(为1),则SCB->BFAR寄存器的值表示引起总线Fault的内存地址。

如果SCB->CFSR寄存器的MMFARVALID位有效(为1),则SCB->MMFAR寄存器的值表示引起存储器管理Fault的内存地址。

参考资料:

  1. ARM Cortex-M3权威指南 Joseph Yiu著 宋岩译
  2. Application Note 209 Using Cortex-M3 and Cortex -M4 Fault exceptions Copyright 2010 ARM
  3. LPC178x/7x用户手册 NXP
更多Arm技术文章动态请关注极术社区Arm技术专栏
推荐阅读
关注数
23397
内容数
896
Arm相关的技术博客,提供最新Arm技术干货,欢迎关注
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息