Cortex-A/R Fault

From SEGGER Wiki
Revision as of 08:59, 13 April 2023 by Martin (talk | contribs)
Jump to: navigation, search

This article describes the analysis of fault exceptions on Cortex-A and Cortex-R CPUs.

Cortex-A and Cortex-R Exceptions

On Cortex-A and Cortex-R CPUs, an exception causes the processor to suspend program execution to handle an event, e.g. an externally generated interrupt or an attempt to execute an undefined instruction.

When an exception is taken, processor execution jumps to an exception vector that matches the type of exception that has occurred.
By default, the exception vectors are eight consecutive word-aligned memory addresses, starting at an exception base address:

Vector offset Vectored exception
0x00 Reset
0x04 Undefined Instruction
0x08 Secure Monitor Call (SMC)
Supervisor Call (SVC)
0x0C Prefetch Abort
0x10 Data Abort
0x14 Reserved
0x18 Interrupt (IRQ)
0x1C Fast Interrupt (FIQ)

Of these exceptions, the Undefined Instruction exception, the Prefetch Abort exception, and the Data Abort exception are considered fault exceptions.
Other types of exceptions, such as SVC, IRQ, and FIQ are not covered by this article.

Undefined Instruction Exception

An Undefined Instruction exception might be caused by

  • a co-processor instruction that is not accessible
  • a co-processor instruction that is not implemented
  • an instruction that is undefined
  • an attempt to execute an instruction in an unsupported instruction set state
  • division by zero

Prefetch Abort Exception

A Prefetch Abort exception can be generated by

  • a synchronous memory abort on an instruction fetch
  • a Breakpoint, Vector Catch or BKPT Instruction debug event

Data Abort Exception

A Data Abort exception can be generated by

  • a synchronous abort on a data read or write memory access
  • an asynchronous abort on a data read or write access
  • an asynchronous abort on an instruction fetch or prefetch
  • an asynchronous abort on a translation table access
  • a Watchpoint debug event

Fault Exception Handling

Fault exception handling is usually done differently during development and in production firmware.

Fault Exception Handling During Development

During development of a firmware, errors can happen. Developers usually want to analyze what went wrong to resolve the error.

The Cortex-A and Cortex-R CPUs provide various registers to analyze the reason of Prefetch Abort and Data Abort exceptions.
Exception handlers may contain development code to gather the contents of these registers in order to easily make them available in a debugger.
The SEGGER Prefetch Abort and Data Abort Handlers listed below read the corresponding fault registers and make them accessible in a single, comprehensive struct.

The only mechanism to determine the cause of an Undefined Instruction exception is analysis of the instruction indicated by the return link in the LR on exception entry.

Fault Exception Handling in Productive Firmware

Exceptions might also occur in release firmwares and should be caught.

Some exceptions might not be caused by an error.
For example executing a BKPT instruction when no debugger is connected causes a Prefetch Abort.
Firmware may simply return from such exception and continue program execution.

The system might also want to recover from certain cases which are caused by errors, but are recoverable.
In the exception handler, the error reason can be analyzed and, for example, terminate the task that has caused the error.

In the case of a unrecoverable error, the system could recover e.g. by simply resetting it. In more advanced scenarios, a crash dump could be generated before the reset, to be send or saved on reboot.

Fault Status Registers

Instruction Fault Status Register

The Instruction Fault Status Register (IFSR) is a 32-bit register and can be accessed by reads to the CP15 registers with <opc1> set to 0, <CRn> set to c5, <CRm set to c0, and <opc2> set to 1:

MRC p15, 0, <Rt>, c5, c0, 1 ; Read IFSR into Rt

The fault status bits contained in this register may be used to determine the source of the Prefetch Abort exception.

Three different formats of the IFSR exist, one for PMSAv7 implementations (Cortex-R) and two for VMSAv7 implementations (Cortex-A).
The latter depend on whether the short-descriptor translation table format or the long-descriptor translation table format is used by the implementation.

IFSR format in a PMSAv7 implementation

Bit(s) Description
[3:0] Fault status bits[3:0].
[9:4] UNK/SBZP.
[10] Fault status bit[4].
[11] UNK/SBZP.
[12] External abort type. This bit can provide an implementation defined classification of external aborts.
For aborts other than external aborts this bit always returns 0.
[31:13] UNK/SBZP.

In this format, the fault status bits ([10, 3:0]) encode the source of the Prefetch Abort exception as follows.
All encodings not shown in the table are reserved.

Value Source
0x00 Background fault (MPU fault).
0x01 Alignment fault.
0x02 Debug event that generates a Prefetch Abort exception.
0x08 Synchronous external abort.
0x0D Permission fault (MPU fault).
0x14 Implementation defined (Lock-down).
0x19 Synchronous parity error on memory access.
0x1A Implementation defined (Co-processor abort).

IFSR format when using the Short-descriptor translation table format in a VMSAv7 implementation

Bit(s) Description
[3:0] Fault status bits[3:0]
[8:4] UNK/SBZP.
[9] If the implementation does not include the Large Physical Address Extension:
UNK/SBZP.
If the implementation includes the Large Physical Address Extension:
On taking a Prefetch Abort exception, this bit is set to 0 to indicate use of the Short-descriptor translation table format.
[10] Fault status bit[4].
[11] UNK/SBZP.
[12] External abort type. This bit can provide an implementation defined classification of external aborts.
For aborts other than external aborts this bit always returns 0.
[31:13] UNK/SBZP.

In this format, the fault status bits ([10, 3:0]) encode the source of the Prefetch Abort exception as follows.
All encodings not shown in the table are reserved.

Value Source
0x02 Debug event.
0x03 Access flag fault (MMU fault). First level.
0x05 Translation fault (MMU fault). First level.
0x06 Access flag fault (MMU fault). Second level.
0x07 Translation fault (MMU fault). Second level.
0x08 Synchronous external abort.
0x09 Domain fault (MMU fault). First level.
0x0B Domain fault (MMU fault). Second level.
0x0C Synchronous external abort on translation table walk. First level.
0x0D Permission fault (MMU fault). First level.
0x0E Synchronous external abort on translation table walk. Second level.
0x0F Permission fault (MMU fault). Second level.
0x10 TLB conflict abort.
0x14 Implementation defined (Lock-down).
0x19 Synchronous parity error on memory access.
0x1A Implementation defined (Co-processor abort).
0x1C Synchronous parity error on translation table walk. First level.
0x1E Synchronous parity error on translation table walk. Second level.

IFSR format when using the Long-descriptor translation table format in a VMSAv7 implementation

Bit(s) Description
[5:0] Fault status bits.
[8:6] UNK/SBZP.
[9] On taking a Prefetch Abort exception, this bit is set to 1 to indicate use of the Long-descriptor translation table format.
[11:10] UNK/SBZP.
[12] External abort type. This bit can provide an implementation defined classification of external aborts.
For aborts other than external aborts this bit always returns 0.
[31:13] UNK/SBZP.

In this format, the fault status bits ([5:0]) encode the source of the Prefetch Abort exception as follows.
All encodings not shown in the table are reserved.

Value Source
0x05 Translation fault (MMU fault). First level.
0x06 Translation fault (MMU fault). Second level.
0x07 Translation fault (MMU fault). Third level.
0x09 Access flag fault (MMU fault). First level.
0x0A Access flag fault (MMU fault). Second level.
0x0B Access flag fault (MMU fault). Third level.
0x0D Permission fault (MMU fault). First level.
0x0E Permission fault (MMU fault). Second level.
0x0F Permission fault (MMU fault). Third level.
0x10 Synchronous external abort.
0x15 Synchronous external abort on translation table walk. First level.
0x16 Synchronous external abort on translation table walk. Second level.
0x17 Synchronous external abort on translation table walk. Third level.
0x18 Synchronous parity error on memory access.
0x1D Synchronous parity error on memory access on translation table walk. First level.
0x1E Synchronous parity error on memory access on translation table walk. Second level.
0x1F Synchronous parity error on memory access on translation table walk. Third level.
0x21 Alignment fault.
0x22 Debug event.
0x30 TLB conflict abort.
0x3D Domain fault. First level.
0x3E Domain fault. Second level.

Instruction Fault Address Register

The Instruction Fault Address Register (IFAR) is a 32-bit register and can be accessed by reads to the CP15 registers with <opc1> set to 0, <CRn> set to c6, <CRm> set to c0, and <opc2> set to 2:

MRC p15, 0, <Rt>, c6, c0, 2 ; Read IFAR into Rt

The IFAR holds the address (PMSAv7) / virtual address (VMSAv7) of the faulting access that caused a synchronous Prefetch Abort exception.
Its content is invalid e.g. for asynchronous Prefetch Abort exceptions.

Auxiliary Instruction Fault Status Register

The Auxiliary Instruction Fault Status Register (AIFSR) is a 32-bit register and can be accessed by reads to the CP15 registers with <opc1> set to 0, <CRn> set to c5, <CRm set to c1, and <opc2> set to 1:

MRC p15, 0, <Rt>, c5, c1, 1 ; Read AIFSR into Rt

The content and use of this register is implementation defined.
An implementation can use this register to return additional fault status information.

Data Fault Status Register

The Data Fault Status Register (DFSR) is a 32-bit register and can be accessed by reads to the CP15 registers with <opc1> set to 0, <CRn> set to c5, <CRm set to c0, and <opc2> set to 0:

MRC p15, 0, <Rt>, c5, c0, 0 ; Read DFSR into Rt

The fault status bits contained in this register may be used to determine the source of the Data Abort exception.

Three different formats of the DFSR exist, one for PMSAv7 implementations (Cortex-R) and two for VMSAv7 implementations (Cortex-A).
The latter depend on whether the short-descriptor translation table format or the long-descriptor translation table format is used by the implementation.

DFSR format in a PMSAv7 implementation

Bit(s) Description
[3:0] Fault status bits[3:0].
[9:4] UNK/SBZP.
[10] Fault status bit[4].
[11] Write not Read bit.
On a synchronous exception, indicates whether the abort was caused by a write or a read access.
For faults on CP15 cache maintenance operations, including the address translation operations, this bit always returns a value of 1.
This bit is unknown on an asynchronous Data Abort exception, or on a Data Abort exception caused by a debug exception.
[12] External abort type.
This bit can provide an implementation defined classification of external aborts.
For aborts other than external aborts this bit always returns 0.
[31:13] UNK/SBZP.

In this format, the fault status bits ([10, 3:0]) encode the source of the Data Abort exception as follows.
All encodings not shown in the table are reserved.

Value Source
0x00 Background fault (MPU fault).
0x01 Alignment fault.
0x02 (A)synchronous Watchpoint debug event.
0x08 Synchronous external abort.
0x0D Permission fault (MPU fault).
0x14 Implementation defined (Lock-down).
0x16 Asynchronous external abort.
0x18 Asynchronous parity error on memory access.
0x19 Synchronous parity error on memory access.
0x1A Implementation defined (Co-processor abort).

DFSR format when using the Short-descriptor translation table format in a VMSAv7 implementation

Bit(s) Description
[3:0] Fault status bits[3:0].
[7:4] The domain of the fault address. From ARMv7, ARM deprecates any use of this field.
This field is unknown on a Data Abort exception caused by a debug exception, or caused by a Permission fault in an implementation includes the Large Physical Address Extension.
[8] UNK/SBZP.
[9] If the implementation does not include the Large Physical Address Extension:
UNK/SBZP.
If the implementation includes the Large Physical Address Extension:
On taking a Data Abort exception, this bit is set to 0 to indicate use of the Short-descriptor translation table formats.
[10] Fault status bit[4].
[11] Write not Read bit.
On a synchronous exception, indicates whether the abort was caused by a write or a read access.
For faults on CP15 cache maintenance operations, including the address translation operations, this bit always returns a value of 1.
This bit is unknown on an asynchronous Data Abort exception, or on a Data Abort exception caused by a debug exception.
[12] External abort type. This bit can provide an implementation defined classification of external aborts.
For aborts other than external aborts this bit always returns 0.
[13] If the implementation does not include the Large Physical Address Extension:
UNK/SBZP.
If the implementation includes the Large Physical Address Extension:
Cache maintenance fault.
For synchronous faults, this bit indicates whether a cache maintenance operation generated the fault.
On an asynchronous fault, this bit is unknown.
[31:14] UNK/SBZP.

In this format, the fault status bits ([10, 3:0]) encode the source of the Data Abort exception as follows.
All encodings not shown in the table are reserved.

Value Source
0x01 Alignment fault.
0x02 Debug event.
0x03 Access flag fault (MMU fault). First level.
0x04 Fault on instruction cache maintenance.
0x05 Translation fault (MMU fault). First level.
0x06 Access flag fault (MMU fault). Second level.
0x07 Translation fault (MMU fault). Second level.
0x08 Synchronous external abort.
0x09 Domain fault (MMU fault). First level.
0x0B Domain fault (MMU fault). Second level.
0x0C Synchronous external abort on translation table walk. First level.
0x0D Permission fault (MMU fault). First level.
0x0E Synchronous external abort on translation table walk. Second level.
0x0F Permission fault (MMU fault). Second level.
0x10 TLB conflict abort.
0x14 Implementation defined (Lock-down).
0x16 Asynchronous external abort.
0x18 Asynchronous parity error on memory access.
0x19 Synchronous parity error on memory access.
0x1A Implementation defined (Co-processor abort).
0x1C Synchronous parity error on translation table walk. First level.
0x1E Synchronous parity error on translation table walk. Second level.

DFSR format when using the Long-descriptor translation table format in a VMSAv7 implementation

Bit(s) Description
[5:0] Fault status bits.
[8:6] UNK/SBZP.
[9] On taking a Data Abort exception, this bit is set to 1 to indicate use of the Long-descriptor translation table formats.
[10] UNK/SBZP.
[11] Write not Read bit.
On a synchronous exception, indicates whether the abort was caused by a write or a read access.
For faults on CP15 cache maintenance operations, including the address translation operations, this bit always returns a value of 1.
This bit is unknown on an asynchronous Data Abort exception, or on a Data Abort exception caused by a debug exception.
[12] External abort type.
This bit can provide an implementation defined classification of external aborts.
For aborts other than external aborts this bit always returns 0.
[13] Cache maintenance fault.
For synchronous faults, this bit indicates whether a cache maintenance operation generated the fault.
On an asynchronous fault, this bit is unknown.
[31:14] UNK/SBZP.

In this format, the fault status bits ([5:0]) encode the source of the Data Abort exception as follows.
All encodings not shown in the table are reserved.

Value Source
0x05 Translation fault (MMU fault). First level.
0x06 Translation fault (MMU fault). Second level.
0x07 Translation fault (MMU fault). Third level.
0x09 Access flag fault (MMU fault). First level.
0x0A Access flag fault (MMU fault). Second level.
0x0B Access flag fault (MMU fault). Third level.
0x0D Permission fault (MMU fault). First level.
0x0E Permission fault (MMU fault). Second level.
0x0F Permission fault (MMU fault). Third level..
0x10 Synchronous external abort.
0x11 Asynchronous external abort.
0x15 Synchronous external abort on translation table walk. First level.
0x16 Synchronous external abort on translation table walk. Second level.
0x17 Synchronous external abort on translation table walk. Third level.
0x18 Synchronous parity error on memory access.
0x19 Asynchronous parity error on memory access.
0x1D Synchronous parity error on memory access on translation table walk. First level.
0x1E Synchronous parity error on memory access on translation table walk. Second level.
0x1F Synchronous parity error on memory access on translation table walk. Third level.
0x21 Alignment fault.
0x22 Debug event.
0x30 TLB conflict abort.
0x34 Implementation defined (Lock-down).
0x3A Implementation defined (Co-processor abort).
0x3D Domain fault. First level.
0x3E Domain fault. Second level.

Data Fault Address Register

The Data Fault Address Register (DFAR) is a 32-bit register and can be accessed by reads to the CP15 registers with <opc1> set to 0, <CRn> set to c6, <CRm> set to c0, and <opc2> set to 0:

MRC p15, 0, <Rt>, c6, c0, 0 ; Read DFAR into Rt

The DFAR holds the address (PMSAv7) / virtual address (VMSAv7) of the faulting access that caused a synchronous Data Abort exception.
Its content is invalid e.g. for asynchronous Data Abort exceptions.

Auxiliary Data Fault Status Register

The Auxiliary Data Fault Status Register (ADFSR) is a 32-bit register and can be accessed by reads to the CP15 registers with <opc1> set to 0, <CRn> set to c5, <CRm set to c1, and <opc2> set to 0:

MRC p15, 0, <Rt>, c5, c1, 0 ; Read ADFSR into Rt

The content and use of this register is implementation defined.
An implementation can use this register to return additional fault status information.

Example Fault analysis

Unaligned Access

/*********************************************************************
*
*       _UnalignedAccess()
*
*  Function description
*   Trigger a Data Abort exception by an unaligned word access.
*
*  Additional Information
*    Data Abort is triggered immediately on the read instruction.
*
*    DAbortRegs               <struct>
*    - DFSR                   <struct>        
*      - DFSR.word            0x000000f1
*      - DFSR.bits_short      <struct>
*        - FS_3_0             0x1      
*        - Domain             0xf
*        - LPAE               0x0                <--- Short format is used
*        - FS_4               0x0    
*        - WnR                0x0                <--- Fault caused by read access
*        - ExT                0x0
*        - CM                 0x0  
*      - DFSR.bits_long       <struct>
*      - FaultSource_Short    S_ALIGNMENT_FAULT  <--- Alignment fault detected
*      - FaultSource_Long     L_FORMAT_NOT_USED
*    - DFAR                   <struct>
*      - DFAR                 0x0013ffff         <--- Fault was caused by accessing this address
*      - Valid                VALID              <--- Value contained in DFAR is valid on alignment faults
*    - ADFSR                  0x00000000
*    - ExceptionReturn        0xfc029982         <--- Address that holds the faulting instruction
*/
static int _UnalignedAccess(void) {
  int                    r;
  volatile unsigned int* p;

  p = (unsigned int*)0x0013FFFF;
  //  F64F70FF    movw r0, #0xFFFF
  //  F2C00013    movt r0, #19     <--- Not a word aligned address
  r = *p;
  //  6800        ldr r0, [r0]     <--- Load word from unaligned address raises exception
  return r;
}

File listing

The system state that led to a Prefetch Abort or a Data Abort exception can be analyzed with the SEGGER Prefetch Abort and Data Abort Handlers, which collect the contents of the exception fault registers described above.
On the other hand, in order to determine the cause of an Undefined Instruction exception, applications should analyze the instruction indicated by the return link in the LR on exception entry.

SEGGER Prefetch Abort Handler

/*********************************************************************
*                     SEGGER Microcontroller GmbH                    *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*       (c) 1995 - 2023 SEGGER Microcontroller GmbH                  *
*                                                                    *
*       Internet: segger.com  Support: support_embos@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_PAbortHandler.c
Purpose : Generic SEGGER Prefetch Abort handler for Cortex-A/R

Additional information:
  This Prefetch Abort handler enables user-friendly analysis of
  Prefetch Aborts in debug configurations.
  If a release configuration requires handling of Prefetch Aborts,
  a specific Prefetch Abort handler should be included instead,
  which for example issues a reset or turns on an error LED.

Literature:
  [1] ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition
      \\fileserver.segger.local\Techinfo\Company\ARM\ArchitectureV7\DDI0406C_d_ARMv7-AR_ArchitectureReference.pdf
*/

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/

#ifndef   DEBUG       // Should be overwritten by project settings
  #define DEBUG  (0)  // in debug builds
#endif

#if   ((defined(__SEGGER_CC__) && ( defined(__ARM_ARCH_7A__)                      )) \
 ||    (defined(__clang__)     && ( defined(__ARM_ARCH_7A__)                      )) \
 ||    (defined(__GNUC__)      && ( defined(__ARM_ARCH_7A__)                      )) \
 ||    (defined(__ICCARM__)    && ((defined(__ARM7A__) && (__CORE__ == __ARM7A__)))) \
 ||    (defined(__CC_ARM)      && ( defined(__TARGET_ARCH_7_A)                    )))
  #define ARMv7A  (1)
#elif ((defined(__SEGGER_CC__) && ( defined(__ARM_ARCH_7R__)                      )) \
 ||    (defined(__clang__)     && ( defined(__ARM_ARCH_7R__)                      )) \
 ||    (defined(__GNUC__)      && ( defined(__ARM_ARCH_7R__)                      )) \
 ||    (defined(__ICCARM__)    && ((defined(__ARM7R__) && (__CORE__ == __ARM7R__)))) \
 ||    (defined(__CC_ARM)      && ( defined(__TARGET_ARCH_7_R)                    )))
  #define ARMv7R  (1)
#else
  #error "This handler currently supports ARMv7-A and ARMv7-R only."
#endif

/*********************************************************************
*
*       Typedefs
*
**********************************************************************
*/

#if (ARMv7A == 1)
/*********************************************************************
*
*       VMSAv7 instruction fault sources when address translation
*       is using the Short-descriptor translation table format.
*       See [1]: Table B3-23, Short-descriptor format FSR encodings
*/
typedef enum {
  S_DEBUG_EVENT               = 0x02u,  // Debug event.
  S_ACCESS_FLAG_FAULT_L1      = 0x03u,  // Access flag fault (MMU fault). First level.
  S_TRANSLATION_FAULT_L1      = 0x05u,  // Translation fault (MMU fault). First level.
  S_ACCESS_FLAG_FAULT_L2      = 0x06u,  // Access flag fault (MMU fault). Second level.
  S_TRANSLATION_FAULT_L2      = 0x07u,  // Translation fault (MMU fault). Second level.
  S_SYNC_EXTERNAL_ABORT       = 0x08u,  // Synchronous external abort.
  S_DOMAIN_FAULT_L1           = 0x09u,  // Domain fault (MMU fault). First level.
  S_DOMAIN_FAULT_L2           = 0x0Bu,  // Domain fault (MMU fault). Second level.
  S_SYNC_EXTERNAL_ABORT_TT_L1 = 0x0Cu,  // Synchronous external abort on translation table walk. First level.
  S_PERMISSION_FAULT_L1       = 0x0Du,  // Permission fault (MMU fault). First level.
  S_SYNC_EXTERNAL_ABORT_TT_L2 = 0x0Eu,  // Synchronous external abort on translation table walk. Second level.
  S_PERMISSION_FAULT_L2       = 0x0Fu,  // Permission fault (MMU fault). Second level.
  S_TLB_CONFLICT_ABORT        = 0x10u,  // TLB conflict abort.
  S_LOCKDOWN                  = 0x14u,  // Implementation defined (Lock-down).
  S_SYNC_PARITY_ERROR         = 0x19u,  // Synchronous parity error on memory access.
  S_COPROCESSOR_ABORT         = 0x1Au,  // Implementation defined (Co-processor abort).
  S_SYNC_PARITY_ERROR_TT_L1   = 0x1Cu,  // Synchronous parity error on translation table walk. First level.
  S_SYNC_PARITY_ERROR_TT_L2   = 0x1Eu,  // Synchronous parity error on translation table walk. Second level.
  S_FORMAT_NOT_USED           = 0xDEADBEEFu
} INSTRUCTION_FAULT_SOURCE_SHORT_FORMAT;

/*********************************************************************
*
*       VMSAv7 instruction fault sources when address translation
*       is using the long-descriptor translation table format.
*       See [1]: Table B3-24, Long-descriptor format FSR encodings
*       and Table B3-25, Use of LL bits to encode the lookup level
*/
typedef enum {
  L_TRANSLATION_FAULT_L1      = 0x05u,  // Translation fault (MMU fault). First level.
  L_TRANSLATION_FAULT_L2      = 0x06u,  // Translation fault (MMU fault). Second level.
  L_TRANSLATION_FAULT_L3      = 0x07u,  // Translation fault (MMU fault). Third level.
  L_ACCESS_FLAG_FAULT_L1      = 0x09u,  // Access flag fault (MMU fault). First level.
  L_ACCESS_FLAG_FAULT_L2      = 0x0Au,  // Access flag fault (MMU fault). Second level.
  L_ACCESS_FLAG_FAULT_L3      = 0x0Bu,  // Access flag fault (MMU fault). Third level.
  L_PERMISSION_FAULT_L1       = 0x0Du,  // Permission fault (MMU fault). First level.
  L_PERMISSION_FAULT_L2       = 0x0Eu,  // Permission fault (MMU fault). Second level.
  L_PERMISSION_FAULT_L3       = 0x0Fu,  // Permission fault (MMU fault). Third level.
  L_SYNC_EXTERNAL_ABORT       = 0x10u,  // Synchronous external abort.
  L_SYNC_EXTERNAL_ABORT_TT_L1 = 0x15u,  // Synchronous external abort on translation table walk. First level.
  L_SYNC_EXTERNAL_ABORT_TT_L2 = 0x16u,  // Synchronous external abort on translation table walk. Second level.
  L_SYNC_EXTERNAL_ABORT_TT_L3 = 0x17u,  // Synchronous external abort on translation table walk. Third level.
  L_SYNC_PARITY_ERROR         = 0x18u,  // Synchronous parity error on memory access.
  L_SYNC_PARITY_ERROR_TT_L1   = 0x1Du,  // Synchronous parity error on memory access on translation table walk. First level.
  L_SYNC_PARITY_ERROR_TT_L2   = 0x1Eu,  // Synchronous parity error on memory access on translation table walk. Second level.
  L_SYNC_PARITY_ERROR_TT_L3   = 0x1Fu,  // Synchronous parity error on memory access on translation table walk. Third level.
  L_ALIGNMENT_FAULT           = 0x21u,  // Alignment fault.
  L_DEBUG_EVENT               = 0x22u,  // Debug event.
  L_TLB_CONFLICT_ABORT        = 0x30u,  // TLB conflict abort.
  L_DOMAIN_FAULT_L1           = 0x3Du,  // Domain fault. First level.
  L_DOMAIN_FAULT_L2           = 0x3Eu,  // Domain fault. Second level.
  L_FORMAT_NOT_USED           = 0xDEADBEEFu
} INSTRUCTION_FAULT_SOURCE_LONG_FORMAT;

#elif (ARMv7R == 1)
/*********************************************************************
*
*       PMSAv7 instruction fault sources
*       See [1]: Table B5-7, PMSAv7 IFSR encodings
*/
typedef enum {
  BACKGROUND_FAULT    = 0x00u,  // Background fault (MPU fault).
  ALIGNMENT_FAULT     = 0x01u,  // Alignment fault.
  DEBUG_EVENT         = 0x02u,  // Debug event that generates a Prefetch Abort exception.
  SYNC_EXTERNAL_ABORT = 0x08u,  // Synchronous external abort.
  PERMISSION_FAULT    = 0x0Du,  // Permission fault (MPU fault).
  LOCKDOWN            = 0x14u,  // Implementation defined (Lock-down).
  SYNC_PARITY_ERROR   = 0x19u,  // Synchronous parity error on memory access.
  COPROCESSOR_ABORT   = 0x1Au   // Implementation defined (Co-processor abort).
} INSTRUCTION_FAULT_SOURCE;

#endif  // ARMv7A or ARMv7R

typedef enum {
  INVALID = 0x0u,
  VALID   = 0x1u
} IFAR_VALID;

/*********************************************************************
*
*       Prototypes
*
**********************************************************************
*/
#ifdef __cplusplus
  extern "C" {
#endif
void pabort_handler(void);
#ifdef __cplusplus
  }
#endif

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/
#if (DEBUG != 0)
static volatile unsigned int _Continue;  // Set this variable to 1 to run further

volatile static struct {
#if (ARMv7A == 1)
  //
  // The following struct is for a VMSAv7 implementation.
  // It covers the short-descriptor translation table format (bits_short) and the long-descriptor translation table format (bits_long) for a VMSAv7 implementation that does include the Large Physical Address Extension.
  // For a VMSAv7 implementation that does not include the Large Physical Address Extension, this pabort_handler() implementation will use bits_short, too, since that format only differs in the additional LPAE bit.
  //
  struct {
    union {
      unsigned int word;
      //
      // See [1]: B4.1.96 IFSR, Instruction Fault Status Register, VMSA; IFSR format when using the Short-descriptor translation table format
      //
      struct {
        unsigned int FS_3_0 :  4;  //   [3:0] Fault status bits[3:0].
        unsigned int        :  5;  //   [8:4] UNK/SBZP.
        unsigned int LPAE   :  1;  //     [9] If the implementation does not include the Large Physical Address Extension: UNK/SBZP. If the implementation includes the Large Physical Address Extension: On taking an exception, this bit is set to 0 to indicate use of the Short-descriptor translation table format. Hardware does not interpret this bit to determine the behavior of the memory system, and therefore software can set this bit to 0 or 1 without affecting operation. Unless the register has been updated to report a fault, a subsequent read of the register returns the value written to it.
        unsigned int FS_4   :  1;  //    [10] Fault status bit[4].
        unsigned int        :  1;  //    [11] UNK/SBZP.
        unsigned int ExT    :  1;  //    [12] External abort type. This bit can provide an implementation defined classification of external aborts. For aborts other than external aborts this bit always returns 0.
        unsigned int        : 19;  // [31:13] UNK/SBZP.
      } bits_short;
      //
      // See [1]: B4.1.96 IFSR, Instruction Fault Status Register, VMSA; IFSR format when using the Long-descriptor translation table format
      //
      struct {
        unsigned int Status :  6;  //   [5:0] Fault status bits.
        unsigned int        :  3;  //   [8:6] UNK/SBZP.
        unsigned int LPAE   :  1;  //     [9] On taking an exception, this bit is set to 1 to indicate use of the Long-descriptor translation table format. Hardware does not interpret this bit to determine the behavior of the memory system, and therefore software can set this bit to 0 or 1 without affecting operation. Unless the register has been updated to report a fault, a subsequent read of the register returns the value written to it.
        unsigned int        :  2;  // [11:10] UNK/SBZP.
        unsigned int ExT    :  1;  //    [12] External abort type. This bit can provide an implementation defined classification of external aborts. For aborts other than external aborts this bit always returns 0.
        unsigned int        : 19;  // [31:13] UNK/SBZP.
      } bits_long;
    } IFSR;
    INSTRUCTION_FAULT_SOURCE_SHORT_FORMAT FaultSource_Short;  // Decoded Fault Source IFSR.FS[4:0], if applicable.
    INSTRUCTION_FAULT_SOURCE_LONG_FORMAT  FaultSource_Long ;  // Decoded Fault Source IFSR.Status, if applicable.
  } IFSR;
#elif (ARMv7R == 1)
  //
  // The following struct is for a PMSAv7 implementation.
  //
  struct {
    union {
      unsigned int word;
      //
      // See [1]: B6.1.59 IFSR, Instruction Fault Status Register, PMSA
      //
      struct {
        unsigned int FS_3_0 :  4;  //   [3:0] Fault status bits[3:0].
        unsigned int        :  6;  //   [9:4] UNK/SBZP.
        unsigned int FS_4   :  1;  //    [10] Fault status bit[4].
        unsigned int        :  1;  //    [11] UNK/SBZP.
        unsigned int ExT    :  1;  //    [12] External abort type. This bit can provide an implementation defined classification of external aborts. For aborts other than external aborts this bit always returns 0.
        unsigned int        : 19;  // [31:13] UNK/SBZP.
      } bits;
    } IFSR;
    INSTRUCTION_FAULT_SOURCE FaultSource;  // Decoded Fault Source IFSR.FS[4:0].
  } IFSR;
#endif  // ARMv7A or ARMv7R

  struct {
    unsigned int IFAR;           // See [1]: B4.1.95 IFAR, Instruction Fault Address Register, VMSA; and B6.1.59 IFSR, Instruction Fault Status Register, PMSA
    IFAR_VALID   Valid;          // See [1]: Table B3-26, Effect of a fault taken to a PL1 mode on the reporting registers; and Table B5-7, PMSAv7 IFSR encodings
  } IFAR;

  unsigned int AIFSR;            // Auxiliary Instruction Fault Status Register, can return additional implementation defined fault status information.
  unsigned int ExceptionReturn;  // Applications may e.g. use this value to return from this pabort_handler by executing __asm volatile("SUBS pc, lr, #4;"); Whether this is possible depends on the type of prefetch abort and whether the application relies on the proper execution of the instruction that caused the abort.
} PAbortRegs;
#endif  // (DEBUG != 0)

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/

/*********************************************************************
*
*       pabort_handler()
*
*  Function description
*    Prefetch abort handler which is called when
*    a prefetch abort exception occurs.
*/
void pabort_handler(void) {
#if (DEBUG != 0)
#if (ARMv7A == 1)
  //
  // Read Instruction Fault Status register.
  //
  __asm volatile("MRC p15, 0, %[Reg], c5, c0, 1;" : [Reg] "=r" (PAbortRegs.IFSR.IFSR.word) : : "memory");
  //
  // Decode the fault source. Initially check bit[9], LPAE, on whether short or long descriptor format is being used.
  // This bit reads 0 when the short and 1 when the long format is being used.
  //
  if ((PAbortRegs.IFSR.IFSR.word & (1u << 9)) == 0u) {
    //
    // Short descriptor format is being used -> decode IFSR.FS.
    //
    PAbortRegs.IFSR.FaultSource_Short = ((PAbortRegs.IFSR.IFSR.word >> 5) & 0x10u) + (PAbortRegs.IFSR.IFSR.word & 0x0Fu);
    PAbortRegs.IFSR.FaultSource_Long  = L_FORMAT_NOT_USED;
    //
    // Mark if IFAR is valid.
    // See [1]: Table B3-26, Effect of a fault taken to a PL1 mode on the reporting registers
    //
    switch (PAbortRegs.IFSR.FaultSource_Short) {
    case S_ACCESS_FLAG_FAULT_L1:
    case S_TRANSLATION_FAULT_L1:
    case S_ACCESS_FLAG_FAULT_L2:
    case S_TRANSLATION_FAULT_L2:
    case S_SYNC_EXTERNAL_ABORT:
    case S_DOMAIN_FAULT_L1:
    case S_DOMAIN_FAULT_L2:
    case S_SYNC_EXTERNAL_ABORT_TT_L1:
    case S_PERMISSION_FAULT_L1:
    case S_SYNC_EXTERNAL_ABORT_TT_L2:
    case S_PERMISSION_FAULT_L2:
    case S_TLB_CONFLICT_ABORT:
    case S_LOCKDOWN:
    case S_SYNC_PARITY_ERROR:
    case S_COPROCESSOR_ABORT:
    case S_SYNC_PARITY_ERROR_TT_L1:
    case S_SYNC_PARITY_ERROR_TT_L2:
      PAbortRegs.IFAR.Valid = VALID;
      break;
    case S_DEBUG_EVENT:
    default:  // Reserved encoding, should never happen.
      PAbortRegs.IFAR.Valid = INVALID;
      break;
    }
  } else {
    //
    // Long descriptor format is being used -> decode IFSR.Status.
    //
    PAbortRegs.IFSR.FaultSource_Long  = (PAbortRegs.IFSR.IFSR.word & 0x3Fu);
    PAbortRegs.IFSR.FaultSource_Short = S_FORMAT_NOT_USED;
    //
    // Mark if IFAR is valid.
    // See [1]: Table B3-26, Effect of a fault taken to a PL1 mode on the reporting registers
    //
    switch (PAbortRegs.IFSR.FaultSource_Long) {
    case L_TRANSLATION_FAULT_L1:
    case L_TRANSLATION_FAULT_L2:
    case L_TRANSLATION_FAULT_L3:
    case L_ACCESS_FLAG_FAULT_L1:
    case L_ACCESS_FLAG_FAULT_L2:
    case L_ACCESS_FLAG_FAULT_L3:
    case L_PERMISSION_FAULT_L1:
    case L_PERMISSION_FAULT_L2:
    case L_PERMISSION_FAULT_L3:
    case L_SYNC_EXTERNAL_ABORT:
    case L_SYNC_EXTERNAL_ABORT_TT_L1:
    case L_SYNC_EXTERNAL_ABORT_TT_L2:
    case L_SYNC_EXTERNAL_ABORT_TT_L3:
    case L_SYNC_PARITY_ERROR:
    case L_SYNC_PARITY_ERROR_TT_L1:
    case L_SYNC_PARITY_ERROR_TT_L2:
    case L_SYNC_PARITY_ERROR_TT_L3:
    case L_ALIGNMENT_FAULT:
    case L_TLB_CONFLICT_ABORT:
    case L_DOMAIN_FAULT_L1:
    case L_DOMAIN_FAULT_L2:
      PAbortRegs.IFAR.Valid = VALID;
      break;
    case L_DEBUG_EVENT:
    default:  // Reserved encoding, should never happen.
      PAbortRegs.IFAR.Valid = INVALID;
      break;
    }
  }
#elif (ARMv7R == 1)
  //
  // Read Instruction Fault Status register.
  //
  __asm volatile("MRC p15, 0, %[Reg], c5, c0, 1;" : [Reg] "=r" (PAbortRegs.IFSR.IFSR.word) : : "memory");
  //
  // Decode the fault source.
  //
  PAbortRegs.IFSR.FaultSource = ((PAbortRegs.IFSR.IFSR.word >> 5) & 0x10u) + (PAbortRegs.IFSR.IFSR.word & 0x0Fu);
  //
  // Mark if IFAR is valid.
  // See [1]: Table B5-7, PMSAv7 IFSR encodings
  //
  switch (PAbortRegs.IFSR.FaultSource) {
  case BACKGROUND_FAULT:
  case ALIGNMENT_FAULT:
  case SYNC_EXTERNAL_ABORT:
  case PERMISSION_FAULT:
  case SYNC_PARITY_ERROR:
    PAbortRegs.IFAR.Valid = VALID;
    break;
  case DEBUG_EVENT:
  case LOCKDOWN:
  case COPROCESSOR_ABORT:
  default:  // Reserved encoding, should never happen.
    PAbortRegs.IFAR.Valid = INVALID;
    break;
  }
#endif  // ARMv7A or ARMv7R
  //
  // Read Auxiliary Instruction Fault Status Register.
  //
  __asm volatile("MRC p15, 0, %[Reg], c5, c1, 1;" : [Reg] "=r" (PAbortRegs.AIFSR) : : "memory");
  //
  // Read Instruction Fault Address Register.
  //
  __asm volatile("MRC p15, 0, %[Reg], c6, c0, 2;" : [Reg] "=r" (PAbortRegs.IFAR.IFAR) : : "memory");
  //
  // Recover PC value on exception entry.
  //
  __asm volatile("SUB %[Reg], lr, #4;" : [Reg] "=r" (PAbortRegs.ExceptionReturn) : : "memory");
  //
  // Halt execution.
  //
  _Continue = 0u;
  do {
  } while (_Continue == 0u);
#else   // (DEBUG != 0)
  //
  // If this module is included in a release configuration, simply stay in the pabort_handler().
  //
  do {
  } while (1);
#endif
}

/*************************** End of file ****************************/

SEGGER Data Abort Handler

/*********************************************************************
*                     SEGGER Microcontroller GmbH                    *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*       (c) 1995 - 2023 SEGGER Microcontroller GmbH                  *
*                                                                    *
*       Internet: segger.com  Support: support_embos@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_DAbortHandler.c
Purpose : Generic SEGGER Data Abort handler for Cortex-A/R

Additional information:
  This Data Abort handler enables user-friendly analysis of
  Data Aborts in debug configurations.
  If a release configuration requires handling of Data Aborts,
  a specific Data Abort handler should be included instead,
  which for example issues a reset or turns on an error LED.

Literature:
  [1] ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition
      \\fileserver.segger.local\Techinfo\Company\ARM\ArchitectureV7\DDI0406C_d_ARMv7-AR_ArchitectureReference.pdf
*/

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/

#ifndef   DEBUG       // Should be overwritten by project settings
  #define DEBUG  (0)  // in debug builds
#endif

#if   ((defined(__SEGGER_CC__) && ( defined(__ARM_ARCH_7A__)                      )) \
 ||    (defined(__clang__)     && ( defined(__ARM_ARCH_7A__)                      )) \
 ||    (defined(__GNUC__)      && ( defined(__ARM_ARCH_7A__)                      )) \
 ||    (defined(__ICCARM__)    && ((defined(__ARM7A__) && (__CORE__ == __ARM7A__)))) \
 ||    (defined(__CC_ARM)      && ( defined(__TARGET_ARCH_7_A)                    )))
  #define ARMv7A  (1)
#elif ((defined(__SEGGER_CC__) && ( defined(__ARM_ARCH_7R__)                      )) \
 ||    (defined(__clang__)     && ( defined(__ARM_ARCH_7R__)                      )) \
 ||    (defined(__GNUC__)      && ( defined(__ARM_ARCH_7R__)                      )) \
 ||    (defined(__ICCARM__)    && ((defined(__ARM7R__) && (__CORE__ == __ARM7R__)))) \
 ||    (defined(__CC_ARM)      && ( defined(__TARGET_ARCH_7_R)                    )))
  #define ARMv7R  (1)
#else
  #error "This handler currently supports ARMv7-A and ARMv7-R only."
#endif

/*********************************************************************
*
*       Typedefs
*
**********************************************************************
*/

#if (ARMv7A == 1)
/*********************************************************************
*
*       VMSAv7 data fault sources when address translation
*       is using the Short-descriptor translation table format.
*       See [1]: Table B3-23, Short-descriptor format FSR encodings
*/
typedef enum {
  S_ALIGNMENT_FAULT           = 0x01u,  // Alignment fault.
  S_DEBUG_EVENT               = 0x02u,  // Debug event.
  S_ACCESS_FLAG_FAULT_L1      = 0x03u,  // Access flag fault (MMU fault). First level.
  S_INST_MAINTENANCE_FAULT    = 0x04u,  // Fault on instruction cache maintenance.
  S_TRANSLATION_FAULT_L1      = 0x05u,  // Translation fault (MMU fault). First level.
  S_ACCESS_FLAG_FAULT_L2      = 0x06u,  // Access flag fault (MMU fault). Second level.
  S_TRANSLATION_FAULT_L2      = 0x07u,  // Translation fault (MMU fault). Second level.
  S_SYNC_EXTERNAL_ABORT       = 0x08u,  // Synchronous external abort.
  S_DOMAIN_FAULT_L1           = 0x09u,  // Domain fault (MMU fault). First level.
  S_DOMAIN_FAULT_L2           = 0x0Bu,  // Domain fault (MMU fault). Second level.
  S_SYNC_EXTERNAL_ABORT_TT_L1 = 0x0Cu,  // Synchronous external abort on translation table walk. First level.
  S_PERMISSION_FAULT_L1       = 0x0Du,  // Permission fault (MMU fault). First level.
  S_SYNC_EXTERNAL_ABORT_TT_L2 = 0x0Eu,  // Synchronous external abort on translation table walk. Second level.
  S_PERMISSION_FAULT_L2       = 0x0Fu,  // Permission fault (MMU fault). Second level.
  S_TLB_CONFLICT_ABORT        = 0x10u,  // TLB conflict abort.
  S_LOCKDOWN                  = 0x14u,  // Implementation defined (Lock-down).
  S_ASYNC_EXTERNAL_ABORT      = 0x16u,  // Asynchronous external abort.
  S_ASYNC_PARITY_ERROR        = 0x18u,  // Asynchronous parity error on memory access.
  S_SYNC_PARITY_ERROR         = 0x19u,  // Synchronous parity error on memory access.
  S_COPROCESSOR_ABORT         = 0x1Au,  // Implementation defined (Co-processor abort).
  S_SYNC_PARITY_ERROR_TT_L1   = 0x1Cu,  // Synchronous parity error on translation table walk. First level.
  S_SYNC_PARITY_ERROR_TT_L2   = 0x1Eu,  // Synchronous parity error on translation table walk. Second level.
  S_FORMAT_NOT_USED           = 0xDEADBEEFu
} DATA_FAULT_SOURCE_SHORT_FORMAT;

/*********************************************************************
*
*       VMSAv7 data fault sources when address translation
*       is using the long-descriptor translation table format
*       See [1]: Table B3-24, Long-descriptor format FSR encodings
*       and Table B3-25, Use of LL bits to encode the lookup level
*/
typedef enum {
  L_TRANSLATION_FAULT_L1      = 0x05u,  // Translation fault (MMU fault). First level.
  L_TRANSLATION_FAULT_L2      = 0x06u,  // Translation fault (MMU fault). Second level.
  L_TRANSLATION_FAULT_L3      = 0x07u,  // Translation fault (MMU fault). Third level.
  L_ACCESS_FLAG_FAULT_L1      = 0x09u,  // Access flag fault (MMU fault). First level.
  L_ACCESS_FLAG_FAULT_L2      = 0x0Au,  // Access flag fault (MMU fault). Second level.
  L_ACCESS_FLAG_FAULT_L3      = 0x0Bu,  // Access flag fault (MMU fault). Third level.
  L_PERMISSION_FAULT_L1       = 0x0Du,  // Permission fault (MMU fault). First level.
  L_PERMISSION_FAULT_L2       = 0x0Eu,  // Permission fault (MMU fault). Second level.
  L_PERMISSION_FAULT_L3       = 0x0Fu,  // Permission fault (MMU fault). Third level..
  L_SYNC_EXTERNAL_ABORT       = 0x10u,  // Synchronous external abort.
  L_ASYNC_EXTERNAL_ABORT      = 0x11u,  // Asynchronous external abort.
  L_SYNC_EXTERNAL_ABORT_TT_L1 = 0x15u,  // Synchronous external abort on translation table walk. First level.
  L_SYNC_EXTERNAL_ABORT_TT_L2 = 0x16u,  // Synchronous external abort on translation table walk. Second level.
  L_SYNC_EXTERNAL_ABORT_TT_L3 = 0x17u,  // Synchronous external abort on translation table walk. Third level.
  L_SYNC_PARITY_ERROR         = 0x18u,  // Synchronous parity error on memory access.
  L_ASYNC_PARITY_ERROR        = 0x19u,  // Asynchronous parity error on memory access.
  L_SYNC_PARITY_ERROR_TT_L1   = 0x1Du,  // Synchronous parity error on memory access on translation table walk. First level.
  L_SYNC_PARITY_ERROR_TT_L2   = 0x1Eu,  // Synchronous parity error on memory access on translation table walk. Second level.
  L_SYNC_PARITY_ERROR_TT_L3   = 0x1Fu,  // Synchronous parity error on memory access on translation table walk. Third level.
  L_ALIGNMENT_FAULT           = 0x21u,  // Alignment fault.
  L_DEBUG_EVENT               = 0x22u,  // Debug event.
  L_TLB_CONFLICT_ABORT        = 0x30u,  // TLB conflict abort.
  L_LOCKDOWN                  = 0x34u,  // Implementation defined (Lock-down).
  L_COPROCESSOR_ABORT         = 0x3Au,  // Implementation defined (Co-processor abort).
  L_DOMAIN_FAULT_L1           = 0x3Du,  // Domain fault. First level.
  L_DOMAIN_FAULT_L2           = 0x3Eu,  // Domain fault. Second level.
  L_FORMAT_NOT_USED           = 0xDEADBEEFu
} DATA_FAULT_SOURCE_LONG_FORMAT;

#elif (ARMv7R == 1)
/*********************************************************************
*
*       PMSAv7 data fault sources
*       See [1]: Table B5-8, PMSAv7 DFSR encodings
*/
typedef enum {
  BACKGROUND_FAULT     = 0x00u,  // Background fault (MPU fault).
  ALIGNMENT_FAULT      = 0x01u,  // Alignment fault.
  DEBUG_EVENT          = 0x02u,  // (A)synchronous Watchpoint debug event.
  SYNC_EXTERNAL_ABORT  = 0x08u,  // Synchronous external abort.
  PERMISSION_FAULT     = 0x0Du,  // Permission fault (MPU fault).
  LOCKDOWN             = 0x14u,  // Implementation defined (Lock-down).
  ASYNC_EXTERNAL_ABORT = 0x16u,  // Asynchronous external abort.
  ASYNC_PARITY_ERROR   = 0x18u,  // Asynchronous parity error on memory access.
  SYNC_PARITY_ERROR    = 0x19u,  // Synchronous parity error on memory access.
  COPROCESSOR_ABORT    = 0x1Au   // Implementation defined (Co-processor abort).
} DATA_FAULT_SOURCE;

#endif  // ARMv7A or ARMv7R

typedef enum {
  INVALID                = 0x0u,
  VALID                  = 0x1u,
  IMPLEMENTATION_DEFINED = 0x2u
} DFAR_VALID;

/*********************************************************************
*
*       Prototypes
*
**********************************************************************
*/
#ifdef __cplusplus
  extern "C" {
#endif
void dabort_handler(void);
#ifdef __cplusplus
  }
#endif

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/
#if (DEBUG != 0)
static volatile unsigned int _Continue;  // Set this variable to 1 to run further

volatile static struct {
#if (ARMv7A == 1)
  //
  // The following struct is for a VMSAv7 implementation.
  // It covers the short-descriptor translation table format (bits_short) and the long-descriptor translation table format (bits_long) for a VMSAv7 implementation that does include the Large Physical Address Extension.
  // For a VMSAv7 implementation that does not include the Large Physical Address Extension, this dabort_handler() implementation will use bits_short, too, since that format only differs in the additional LPAE and CM bits.
  //
  struct {
    union {
      unsigned int word;
      //
      // See [1]: B4.1.52: DFSR, Data Fault Status Register, VMSA; DFSR format when using the Short-descriptor translation table format
      //
      struct {
        unsigned int FS_3_0 :  4;  //   [3:0] Fault status bits[3:0].
        unsigned int Domain :  4;  //   [7:4] The domain of the fault address. From ARMv7, ARM deprecates any use of this field. This field is unknown on a Data Abort exception caused by a debug exception, or caused by a Permission fault in an implementation includes the Large Physical Address Extension.
        unsigned int        :  1;  //     [8] UNK/SBZP.
        unsigned int LPAE   :  1;  //     [9] If the implementation does not include the Large Physical Address Extension: UNK/SBZP. If the implementation includes the Large Physical Address Extension: On taking a Data Abort exception, this bit is set to 0 to indicate use of the Short-descriptor translation table formats. Hardware does not interpret this bit to determine the behavior of the memory system, and therefore software can set this bit to 0 or 1 without affecting operation. Unless the register has been updated to report a fault, a subsequent read of the register returns the value written to it.
        unsigned int FS_4   :  1;  //    [10] Fault status bit[4].
        unsigned int WnR    :  1;  //    [11] Write not Read bit. On a synchronous exception, indicates whether the abort was caused by a write or a read access. For faults on CP15 cache maintenance operations, including the address translation operations, this bit always returns a value of 1. This bit is unknown on an asynchronous Data Abort exception, or on a Data Abort exception caused by a debug exception.
        unsigned int ExT    :  1;  //    [12] External abort type. This bit can provide an implementation defined classification of external aborts. For aborts other than external aborts this bit always returns 0.
        unsigned int CM     :  1;  //    [13] If the implementation does not include the Large Physical Address Extension: UNK/SBZP. If the implementation includes the Large Physical Address Extension: Cache maintenance fault. For synchronous faults, this bit indicates whether a cache maintenance operation generated the fault. On an asynchronous fault, this bit is unknown.
        unsigned int        : 18;  // [31:14] UNK/SBZP.
      } bits_short;
      //
      // See [1]: B4.1.52: DFSR, Data Fault Status Register, VMSA; DFSR format when using the Long-descriptor translation table format
      //
      struct {
        unsigned int Status :  6;  //   [5:0] Fault status bits.
        unsigned int        :  3;  //   [8:6] UNK/SBZP.
        unsigned int LPAE   :  1;  //     [9] On taking a Data Abort exception, this bit is set to 1 to indicate use of the Long-descriptor translation table formats. Hardware does not interpret this bit to determine the behavior of the memory system, and therefore software can set this bit to 0 or 1 without affecting operation. Unless the register has been updated to report a fault, a subsequent read of the register returns the value written to it.
        unsigned int        :  1;  //    [10] UNK/SBZP.
        unsigned int WnR    :  1;  //    [11] Write not Read bit. On a synchronous exception, indicates whether the abort was caused by a write or a read access. For faults on CP15 cache maintenance operations, including the address translation operations, this bit always returns a value of 1. This bit is unknown on an asynchronous Data Abort exception, or on a Data Abort exception caused by a debug exception.
        unsigned int ExT    :  1;  //    [12] External abort type. This bit can provide an implementation defined classification of external aborts. For aborts other than external aborts this bit always returns 0.
        unsigned int CM     :  1;  //    [13] Cache maintenance fault. For synchronous faults, this bit indicates whether a cache maintenance operation generated the fault. On an asynchronous fault, this bit is unknown.
        unsigned int        : 18;  // [31:14] UNK/SBZP.
      } bits_long;
    } DFSR;
    DATA_FAULT_SOURCE_SHORT_FORMAT FaultSource_Short;  // Decoded Fault Source DFSR.FS[4:0], if applicable.
    DATA_FAULT_SOURCE_LONG_FORMAT  FaultSource_Long;   // Decoded Fault Source DFSR.Status, if applicable.
  } DFSR;
#elif (ARMv7R == 1)
  //
  // The following struct is for a PMSAv7 implementation.
  //
  struct {
    union {
      unsigned int word;
      //
      // See [1]: B6.1.34 DFSR, Data Fault Status Register, PMSA
      //
      struct {
        unsigned int FS_3_0 :  4;  //   [3:0] Fault status bits[3:0].
        unsigned int        :  6;  //   [9:4] UNK/SBZP.
        unsigned int FS_4   :  1;  //    [10] Fault status bit[4].
        unsigned int WnR    :  1;  //    [11] Write not Read bit. On a synchronous exception, indicates whether the abort was caused by a write or a read access. For faults on CP15 cache maintenance operations, including the address translation operations, this bit always returns a value of 1. This bit is unknown on an asynchronous Data Abort exception, or on a Data Abort exception caused by a debug exception.
        unsigned int ExT    :  1;  //    [12] External abort type. This bit can provide an implementation defined classification of external aborts. For aborts other than external aborts this bit always returns 0.
        unsigned int        : 19;  // [31:13] UNK/SBZP.
      } bits;
    } DFSR;
    DATA_FAULT_SOURCE FaultSource;  // Decoded Fault Source DFSR.FS[4:0].
  } DFSR;
#endif  // ARMv7A or ARMv7R

  struct {
    unsigned int DFAR;           // See [1]: B4.1.51 DFAR, Data Fault Address Register, VMSA; and B6.1.33 DFAR, Data Fault Address Register, PMSA
    DFAR_VALID   Valid;          // See [1]: Table B3-26, Effect of a fault taken to a PL1 mode on the reporting registers; and Table B5-8, PMSAv7 DFSR encodings
  } DFAR;

  unsigned int ADFSR;            // Auxiliary Data Fault Status Register, can return additional implementation defined fault status information.
  unsigned int ExceptionReturn;  // Applications may e.g. use this value to return from this dabort_handler by executing __asm volatile("SUBS pc, lr, #8;"); Whether this is possible depends on the type of data abort and whether the application relies on the proper execution of the instruction that caused the abort.
} DAbortRegs;
#endif  // (DEBUG != 0)

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/

/*********************************************************************
*
*       dabort_handler()
*
*  Function description
*    Data abort handler which is called when
*    a data abort exception occurs.
*/
void dabort_handler(void) {
#if (DEBUG != 0)
#if (ARMv7A == 1)
  //
  // Read Data Fault Status register.
  //
  __asm volatile("MRC p15, 0, %[Reg], c5, c0, 0;" : [Reg] "=r" (DAbortRegs.DFSR.DFSR.word) : : "memory");
  //
  // Decode the fault source. Initially check bit[9], LPAE, on whether short or long descriptor format is being used.
  // This bit reads 0 when the short and 1 when the long format is being used.
  //
  if ((DAbortRegs.DFSR.DFSR.word & (1u << 9)) == 0u) {
    //
    // Short descriptor format is being used -> decode DFSR.FS.
    //
    DAbortRegs.DFSR.FaultSource_Short = ((DAbortRegs.DFSR.DFSR.word >> 5) & 0x10u) + (DAbortRegs.DFSR.DFSR.word & 0x0Fu);
    DAbortRegs.DFSR.FaultSource_Long  = L_FORMAT_NOT_USED;
    //
    // Mark if DFAR is valid.
    // See [1]: Table B3-26, Effect of a fault taken to a PL1 mode on the reporting registers
    //
    switch (DAbortRegs.DFSR.FaultSource_Short) {
    case S_ALIGNMENT_FAULT:
    case S_ACCESS_FLAG_FAULT_L1:
    case S_INST_MAINTENANCE_FAULT:
    case S_TRANSLATION_FAULT_L1:
    case S_ACCESS_FLAG_FAULT_L2:
    case S_TRANSLATION_FAULT_L2:
    case S_SYNC_EXTERNAL_ABORT:
    case S_DOMAIN_FAULT_L1:
    case S_DOMAIN_FAULT_L2:
    case S_SYNC_EXTERNAL_ABORT_TT_L1:
    case S_PERMISSION_FAULT_L1:
    case S_SYNC_EXTERNAL_ABORT_TT_L2:
    case S_PERMISSION_FAULT_L2:
    case S_TLB_CONFLICT_ABORT:
    case S_LOCKDOWN:
    case S_SYNC_PARITY_ERROR:
    case S_COPROCESSOR_ABORT:
    case S_SYNC_PARITY_ERROR_TT_L1:
    case S_SYNC_PARITY_ERROR_TT_L2:
      DAbortRegs.DFAR.Valid = VALID;
      break;
    case S_DEBUG_EVENT:  // Depends on specific debug event and implemented debug interface.
      DAbortRegs.DFAR.Valid = IMPLEMENTATION_DEFINED;
      break;
    case S_ASYNC_EXTERNAL_ABORT:
    case S_ASYNC_PARITY_ERROR:
    default:             // Reserved encoding, should never happen.
      DAbortRegs.DFAR.Valid = INVALID;
      break;
    }
  } else {
    //
    // Long descriptor format is being used -> decode DFSR.Status.
    //
    DAbortRegs.DFSR.FaultSource_Long = (DAbortRegs.DFSR.DFSR.word & 0x3Fu);
    DAbortRegs.DFSR.FaultSource_Short = S_FORMAT_NOT_USED;
    //
    // Mark if DFAR is valid.
    // See [1]: Table B3-26, Effect of a fault taken to a PL1 mode on the reporting registers
    //
    switch (DAbortRegs.DFSR.FaultSource_Long) {
    case L_TRANSLATION_FAULT_L1:
    case L_TRANSLATION_FAULT_L2:
    case L_TRANSLATION_FAULT_L3:
    case L_ACCESS_FLAG_FAULT_L1:
    case L_ACCESS_FLAG_FAULT_L2:
    case L_ACCESS_FLAG_FAULT_L3:
    case L_PERMISSION_FAULT_L1:
    case L_PERMISSION_FAULT_L2:
    case L_PERMISSION_FAULT_L3:
    case L_SYNC_EXTERNAL_ABORT:
    case L_SYNC_EXTERNAL_ABORT_TT_L1:
    case L_SYNC_EXTERNAL_ABORT_TT_L2:
    case L_SYNC_EXTERNAL_ABORT_TT_L3:
    case L_SYNC_PARITY_ERROR:
    case L_SYNC_PARITY_ERROR_TT_L1:
    case L_SYNC_PARITY_ERROR_TT_L2:
    case L_SYNC_PARITY_ERROR_TT_L3:
    case L_ALIGNMENT_FAULT:
    case L_TLB_CONFLICT_ABORT:
    case L_LOCKDOWN:
    case L_COPROCESSOR_ABORT:
    case L_DOMAIN_FAULT_L1:
    case L_DOMAIN_FAULT_L2:
      DAbortRegs.DFAR.Valid = VALID;
      break;
    case L_DEBUG_EVENT:  // Depends on specific debug event and implemented debug interface.
      DAbortRegs.DFAR.Valid = IMPLEMENTATION_DEFINED;
      break;
    case L_ASYNC_EXTERNAL_ABORT:
    case L_ASYNC_PARITY_ERROR:
    default:  // Reserved encoding, should never happen.
      DAbortRegs.DFAR.Valid = INVALID;
      break;
    }
  }
#elif (ARMv7R == 1)
  //
  // Read Data Fault Status register.
  //
  __asm volatile("MRC p15, 0, %[Reg], c5, c0, 0;" : [Reg] "=r" (DAbortRegs.DFSR.DFSR.word) : : "memory");
  //
  // Decode the fault source.
  //
  DAbortRegs.DFSR.FaultSource = ((DAbortRegs.DFSR.DFSR.word >> 5) & 0x10u) + (DAbortRegs.DFSR.DFSR.word & 0x0Fu);
  //
  // Mark if DFAR is valid.
  // See [1]: Table B5-8, PMSAv7 DFSR encodings
  //
  switch (DAbortRegs.DFSR.FaultSource) {
  case BACKGROUND_FAULT:
  case ALIGNMENT_FAULT:
  case SYNC_EXTERNAL_ABORT:
  case PERMISSION_FAULT:
    DAbortRegs.DFAR.Valid = VALID;
    break;
  case DEBUG_EVENT:        // Depends on specific debug event and implemented debug interface.
  case SYNC_PARITY_ERROR:  // Implementation defined.
    DAbortRegs.DFAR.Valid = IMPLEMENTATION_DEFINED;
    break;
  case LOCKDOWN:
  case ASYNC_EXTERNAL_ABORT:
  case ASYNC_PARITY_ERROR:
  case COPROCESSOR_ABORT:
  default:                 // Reserved encoding, should never happen.
    DAbortRegs.DFAR.Valid = INVALID;
    break;
  }
#endif  // ARMv7A or ARMv7R
  //
  // Read Auxiliary Data Fault Status Register.
  //
  __asm volatile("MRC p15, 0, %[Reg], c5, c1, 0;" : [Reg] "=r" (DAbortRegs.ADFSR) : : "memory");
  //
  // Read Data Fault Address Register.
  //
  __asm volatile("MRC p15, 0, %[Reg], c6, c0, 0;" : [Reg] "=r" (DAbortRegs.DFAR.DFAR) : : "memory");
  //
  // Recover PC value on exception entry.
  //
  __asm volatile("SUB %[Reg], lr, #8;" : [Reg] "=r" (DAbortRegs.ExceptionReturn) : : "memory");
  //
  // Halt execution.
  //
  _Continue = 0u;
  do {
  } while (_Continue == 0u);
#else   // (DEBUG != 0)
  //
  // If this module is included in a release configuration, simply stay in the dabort_handler().
  //
  do {
  } while (1);
#endif
}

/*************************** End of file ****************************/