Cortex-M3和Cortex-M4 Fault異常應用之一 ----- 基礎知識

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; 


2. 定義系統控制寄存器組物理空間基地址

         #define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */
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異常控制寄存器列表:

地址/訪問

寄存器

復位值

描述

0xE000ED14

RW 特權級

SCB->CCR

0x00000000

配置和控制寄存器:包含控制除數爲零和未對齊內存訪問是否觸發用法Fault的使能位。

0xE000ED18

RW 特權級

SCB->SHP[12]

0x00

系統處理程序優先級寄存器:控制異常處理程序的優先級

0xE000ED24

RW特權級

SCB->SHCSR

0x00000000

系統處理程序控制和狀態寄存器

3.1.1 SCB->CCR 寄存器

藍色部分控制是否使能相應的用法Fault

名稱

描述

[31:10]

-

保留

[9]

STKALIGN

表示進入異常時的堆棧對齊。

0:4字節對齊

1:8字節對齊

進入異常時,處理器使用壓入堆棧的PSR位[9]來指示堆棧對齊。從異常返回時,這個堆棧位被用來恢復正確的堆棧對齊。

[8]

BFHFNMIGN

使能時,使得以優先級位-1或-2運行的處理程序忽略加載和存儲指令引起的數據總線故障。它用於硬故障、NMI和FAULTMASK升級處理程序中:

0:加載和存儲指令引起的數據總線故障會引起鎖定。

1:以優先級-1或-2運行的處理程序忽略加載和存儲指令引起的數據總線故障。

僅在處理程序和其數據處於絕對安全的存儲器時將該位設爲1。一般將該位用於探測系統設備和橋接器以檢測並糾正控制路徑問題。

[7:5]

-

保留

[4]

DIV_0_TRP

當處理器進行除0操作(SDIV或UDIV指令)時,會導致故障或停止。

0:不捕獲除以零故障

1:捕獲除以零故障。

當該位設爲0時,除以零返回的商數爲0。

[3]

UNALIGN_TRP

使能非對齊訪問捕獲:

0:不捕獲非對齊半字和字訪問

1:捕獲非對齊半字和字訪問。

如果該位設爲1,非對齊訪問產生一個使用故障。無論UNALIGN_TRP是否設爲1,非對齊的LDM、STM、LDRD和STRD指令總是出錯。

[2]

-

保留

[1]

USERSETM

PEND

 

使能對STIR的無特權軟件訪問。

0:禁能

1:使能

[0]

NONEBASE

THRDENA

 

指示處理器如何進入線程模式:

0:處理器僅在沒有有效異常時才能夠進入線程模式。

1:處理器可以從EXC_RETURN值控制下的任何級別進入線程模式

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異常相關位見下表的藍色部分

名稱

描述

[31:19]

-

保留

[18]

USGFAULTENA

用法Fault使能位,設爲1時使能

[17]

BUSFAULTENA

總線Fault使能位,設爲1時使能

[16]

MEMFAULTENA

存儲器管理Fault使能位,設爲1使能

[15]

SVCALLPENDED

SVC調用掛起位,如果異常掛起,該位讀爲1

[14]

BUSFAULTPENDED

總線Fault異常掛起位,如果異常掛起,該位讀爲1

[13]

MEMFAULTPENDED

存儲器Fault故障異常掛起位,如果異常掛起,該位讀爲1

[12]

USGFAULTPENDED

用法Fault異常掛起位,如果異常掛起,該位讀爲1

[11]

SYSTICKACT

SysTick 異常有效位,如果異常有效,該位讀爲1

[10]

PENDSVACT

PendSV異常有效位,如果異常有效,該位讀爲1

[9]

-

保留

[8]

MONITORACT

調試監控有效位,如果調試監控有效,該位讀爲1

[7]

SVCALLACT

SVC調用有效位,如果SVC調用有效,該位讀爲1

[6:4]

-

保留

[3]

USGFAULTACT

用法Fault異常有效位,如果異常有效,該位讀爲1

[2]

-

保留

[1]

BUSFAULTACT

總線Fault異常有效位,如果異常有效,該位讀爲1

[0]

MEMFAULTACT

存儲器管理Fault異常有效位,如果異常有效,該位讀爲1

      儘管可以寫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的詳細信息以及異常發生時訪問的內存地址。

地址/訪問

寄存器

復位值

描述

0xE000ED28

RW 特權級

SCB->CFSR

0x00000000

可配置Fault狀態寄存器:包含指示存儲器管理Fault、總線Fault或用法Fault的原因位

0xE000ED2C

RW 特權級

SCB->HFSR

0x00000000

硬Fault狀態寄存器:包含用於指示硬Fault原因位。

0xE000ED34

RW特權級

SCB->MMFAR

不可知

存儲器管理Fault地址寄存器:包括產生存儲器管理Fault的位置的地址

0xE000ED38

RW特權級

SCB->BFAR

不可知

總線Fault地址寄存器:包括產生總線Fault的位置的地址

3.2.1 SCB->CFSR寄存器

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

bit31                                                                           bit16

bit15                   bit8

bit7                    bit0

用法Fault狀態寄存器(UFSR)

總線Fault狀態寄存器(BFSR)

存儲器管理Fault狀態寄存器(MMFSR)

SCB->CFSR寄存器可以被分成三個組:

      存儲器管理Fault 狀態寄存器:地址0x0xE000ED28,可以按字節訪問

      總線Fault狀態寄存器:地址0xE000ED29,可以按字節訪問

      用法Fault狀態寄存器:地址0xE000ED2A,可以按半字訪問

3.2.1.1 存儲器管理Fault狀態寄存器MMFSR:指示存儲器訪問Fault的原因

名稱

描述

[7]

MMARVALID

存儲器管理Fault地址寄存器(MMAR)有效標誌:

0:MMAR中的值不是一個有效Fault地址

1:MMAR中保留一個有效Fault地址。

如果發生了一個存儲器管理Fault,並由於優先級的原因升級成一個硬Fault,那麼硬Fault處理程序必須將該位設爲0。

[6:5]

-

保留

[4]

MSTKERR

進入異常時的入棧操作引起的存儲器管理Fault:

0:無入棧Fault

1:進入異常時的入棧操作引起了一個或一個以上的訪問違犯。

當該位設爲1時,依然要對SP進行調節,並且堆棧的上下文區域的值可能不正確。處理器沒有向MMAR中寫入Fault地址。

[3]

MUNSTKERR

異常返回時的出棧操作引起的存儲器管理Fault:

0:無出棧Fault

1:異常返回時的出棧操作已引起一個或一個以上的訪問違犯.

該Fault與處理程序相連,這意味着當該位爲1時,原始的返回堆棧仍然存在。

處理器不能對返回失敗的SP進行調節,並且不會執行新的存儲操作。處理器沒有向MMAR中寫入Fault地址。

[2]

-

保留

[1]

DACCVIOL

數據訪問違犯標誌:

0:無數據訪問違犯Fault

1:處理器試圖在不允許執行操作的位置上進行加載和存儲。

當該位爲1時,異常返回的壓入堆棧的PC值指向出錯指令。處理器已在MMAR中加載了目標訪問的地址。

[0]

IACCVIOL

指令訪問違犯標誌:

0:無指令訪問違犯錯誤

1:處理器試圖從不允許執行操作的位置上進行指令獲取。

即使MPU被禁能,這一故障也會在XN(CM3內核的0xE0000000~0xFFFFFFFF區域)區尋址時發生。

當該位爲1時,異常返回的壓入堆棧的PC值指向出錯指令。處理器沒有向MMAR中寫入故障地址。

3.2.1.2 總線Fault狀態寄存器BFSR:指示總線訪問Fault原因

名稱

描述

[7]

BFARVALID

總線Fault地址寄存器(BFAR)有效標誌:

0:BFAR中的值不是有效故障地址

1:BFAR中保留一個有效故障地址。

在地址已知的總線故障發生後處理器將該位設爲1。該位可以被其他Fault清零,例如之後發生的存儲器管理Fault。

如果發生總線Fault,並由於優先級原因升級爲一個硬Fault,那麼硬Fault處理程序必須將該位設爲0。

[6:5]

-

保留

[4]

STKERR

進入異常時的入棧操作引起的總線Fault:

0:無入棧故障

1:進入異常時的入棧操作已引起一個或一個以上的總線故障。

當處理器將該位設爲1時,依然要對SP進行調節,並且堆棧的上下文區域的值可能不正確。處理器沒有向BFAR中寫入Fault地址。

[3]

UNSTKERR

異常返回時的出棧操作引起的總線Fault:

0:無出棧Fault

1:異常返回時的出棧操作已引起一個或一個以上的總線Fault。

該Fault與處理程序相連, 這意味着當處理器將該位設爲1時,原始的返回堆棧仍然存在。處理器不能對返回失敗的SP進行調節,並且不會執行新的存儲操作,也未向BFAR中寫入Fault地址。

[2]

IMPRECISERR

非精確數據總線錯誤:

0:無非精確數據總線錯誤

1:已發生一個數據總線錯誤,但是堆棧幀中的返回地址與引起錯誤的指令無關。

當處理器將該位設爲1時,不向BFAR中寫入Fault地址。

這是一個異步Fault。因此,如果在當前進程的優先級高於總線Fault優先級時檢測到該Fault,總線Fault被掛起並僅在處理器從所有更高優先級進程中返回時開始變爲有效。如果在處理器進入非精確總線Fault的處理程序前發生一個精確Fault,那麼處理程序同時對IMPRECISERR 和其中一個精確Fault狀態位進行檢測,判斷它們是否置位爲1。

[1]

PRECISERR

精確數據總線錯誤:

0:非精確數據總線錯誤

1:已發生一個數據總線錯誤,且異常返回的壓入堆棧的PC值指向引起Fault的指令。

當處理器將該位設爲1時,向BFAR中寫入Fault地址。

[0]

IBUSERR

指令總線錯誤:

0:無指令總線錯誤

1:指令總線錯誤。

處理器檢測到預取指令時的指令總線錯誤,但僅在其試圖簽發Fault指令時纔將IBUSERR 標誌設爲1。

當處理器將該位設爲1時,不向BFAR中寫入Fault地址。

3.2.1.3 用法Fault狀態寄存器UFSR:指示產生用法Fault的原因

名稱

描述

[15:10]

-

保留

[9]

DIVBYZERO

0:無除以零Fault或除以零捕獲未使能

1:處理器已執行SDIV或UDIV指令(除以零)。

當處理器將該位設爲1時,異常返回的壓入堆棧的PC值指向執行除以零的指令。

注:通過將CCR中的DIV_0_TRP位設爲1使能除以零捕獲,默認是不使能的。

[8]

UNALIGNED

0:無非對齊訪問Fault,或非對齊訪問捕獲未使能

1:處理器已進行了一次非對齊的存儲器訪問。

注:通過將CCR中的UNALIGN_TRP位設爲1來使能非對齊訪問捕獲,默認是不使能的。非對齊的LDM、STM、LDRD和STRD指令總是出錯,與UNALIGN_TRP的設置無關。

[7:4]

-

保留

[3]

NOCP

無協處理器用法Fault。處理器不支持協處理器指令:

0:試圖訪問一個協處理器未引起用法Fault

1:處理器已試圖訪問一個協處理器。

[2]

INVPC

EXC_RETURN的無效PC加載引起的無效PC加載用法Fault:

0:沒有發生無效PC加載用法Fault

1:處理器已試圖將EXC_RETURN非法載入PC,作爲一個無效的上下文或一個無效的EXC_RETURN值。

當該位被設爲1時,異常返回的壓入堆棧的PC值指向嘗試執行非法PC加載的指令。

[1]

INVSTATE

無效狀態用法Fault:

0:未發生無效狀態用法Fault

1:處理器已試圖執行一個非法使用EPSR的指令。

當該位設爲1時,異常返回的壓入堆棧的PC值指向一個嘗試非法使用EPSR的指令。

如果一個未定義的指令使用了EPSR,則該位不被置位爲1。

[0]

UNDEFINSTR

未定義的指令用法Fault:

0:無未定義的指令用法Fault

1:處理器已試圖執行一個未定義的指令。當該位設爲1時,異常返回的壓入堆棧的PC值指向未定義的指令。

未定義的指令是一條不能被處理器譯碼的指令。

3.2.2 SCB->HSFR寄存器

SCB->HSFR寄存器提供關於激活硬Fault處理程序的事件的信息,寫入1清零相應位。

名稱

描述

[31]

DEBUGEVT

硬Fault因調試事件產生,保留供調試使用。對寄存器執行寫操作時,必須向該位寫入0;否則,該行爲不可預知。

[30]

FORCED

指示硬Fault是否由上訪產生,非硬Fault的處理程序無法執行時,會上訪成硬Fault。

0:硬Fault不是因爲非硬Fault上訪產生的

1:硬Fault是通過非硬Fault上訪產生的。

當該位設爲1時,硬Fault處理程序必須讀其他Fault狀態寄存器以找出Fault原因。

[29:2]

-

保留

[1]

VECTTBL

指示一個在異常處理過程中讀向量表而引起的總線Fault:

0:讀向量表未引起總線Fault

1:讀向量表引起了總線Fault。

這一錯誤通常情況下都由硬Fault處理程序來處理。

[0]

-

保留

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
————————————————
版權聲明:本文爲CSDN博主「zhzht19861011」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/zhzht19861011/article/details/8645661

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章