1 //*****************************************************************************
2 //
3 //! @file am_util_faultisr.c
4 //!
5 //! @brief An extended hard-fault handler.
6 //!
7 //! This module is portable to all Ambiq Apollo products with minimal HAL or BSP
8 //! dependencies (SWO output).  It collects the fault information into the sHalFaultData
9 //! structure, which it then prints to stdout (typically SWO).
10 //!
11 //! By default this handler, when included in the build overrides the weak binding of
12 //! the default hardfault handler.  It allocates 512 bytes of global variable space for
13 //! a local stack which guarantees diagnostic output under all hardfault conditions. If
14 //! the local stack is not wanted/needed, remove the macro AM_LOCAL_STACK below. If
15 //! the local stack is disabled, and the SP was invalid at the time of the hardfault,
16 //! a second hardfault can occur before any diagnostic data is collected.
17 //!
18 //! This handler outputs information about the state of the processor at the time
19 //! the hardfault occurred to stdout (typically SWO).  If output is not desired remove
20 //! the macro AM_UTIL_FAULTISR_PRINT below. When prints are disabled, the fault
21 //! information is available in the local sHalFaultData structure.
22 //!
23 //! The handler does not return. After outputting the diagnostic information, it
24 //! spins forever, it does not recover or try and return to the program that caused
25 //! the hardfault.
26 //!
27 //! Upon entry (caused by a hardfault), it switches to a local stack in case
28 //! the cause of the hardfault was a stack related issue.  The stack is sized
29 //! large enough to provide for the local variables and the stack space needed
30 //! for the ouput functions calls being used.
31 //!
32 //! It is compiler/platform independent enabling it to be used with GCC, Keil,
33 //! IAR and easily ported to other tools chains
34 //!
35 //! @addtogroup faultisr FaultISR - Extended Hard Fault ISR
36 //! @ingroup utils
37 //! @{
38 //
39 //*****************************************************************************
40 
41 //*****************************************************************************
42 //
43 // Copyright (c) 2023, Ambiq Micro, Inc.
44 // All rights reserved.
45 //
46 // Redistribution and use in source and binary forms, with or without
47 // modification, are permitted provided that the following conditions are met:
48 //
49 // 1. Redistributions of source code must retain the above copyright notice,
50 // this list of conditions and the following disclaimer.
51 //
52 // 2. Redistributions in binary form must reproduce the above copyright
53 // notice, this list of conditions and the following disclaimer in the
54 // documentation and/or other materials provided with the distribution.
55 //
56 // 3. Neither the name of the copyright holder nor the names of its
57 // contributors may be used to endorse or promote products derived from this
58 // software without specific prior written permission.
59 //
60 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
61 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
62 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
63 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
64 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
65 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
66 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
67 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
68 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
69 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
70 // POSSIBILITY OF SUCH DAMAGE.
71 //
72 // This is part of revision release_sdk_4_4_0-3c5977e664 of the AmbiqSuite Development Package.
73 //
74 //*****************************************************************************
75 
76 #include <stdint.h>
77 #include "am_mcu_apollo.h"
78 
79 //*****************************************************************************
80 //
81 // Macros
82 //
83 //*****************************************************************************
84 
85 #define AM_LOCAL_STACK              // when defined use local stack for HF Diagnostics
86 //#define AM_UTIL_FAULTISR_PRINT      // when defined print output to stdout (SWO)
87 
88 //
89 // Macros used by am_util_faultisr_collect_data().
90 //
91 #define AM_REG_SYSCTRL_CFSR_O                        0xE000ED28
92 #define AM_REG_SYSCTRL_BFAR_O                        0xE000ED38
93 #define AM_REGVAL(x)               (*((volatile uint32_t *)(x)))
94 
95 //
96 // Macros for valid stack ranges.
97 //
98 #if defined(AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4P) || defined(AM_PART_APOLLO4L)
99   #define AM_SP_LOW    SRAM_BASEADDR
100   #define AM_SP_HIGH   (SRAM_BASEADDR + RAM_TOTAL_SIZE)
101 #elif defined(AM_PART_APOLLO3P)
102   #define AM_SP_LOW    SRAM_BASEADDR
103   #define AM_SP_HIGH   (SRAM_BASEADDR + ( 768 * 1024 ))
104 #elif defined(AM_PART_APOLLO3)
105   #define AM_SP_LOW    SRAM_BASEADDR
106   #define AM_SP_HIGH   (SRAM_BASEADDR + ( 384 * 1024 ))
107 #elif defined(AM_PART_APOLLO2)
108   #define AM_SP_LOW     SRAM_BASEADDR
109   #define AM_SP_HIGH   (SRAM_BASEADDR + ( 256 * 1024 ))
110 #elif defined(AM_PART_APOLLO)
111   #define AM_SP_LOW     SRAM_BASEADDR
112   #define AM_SP_HIGH   (SRAM_BASEADDR + ( 64 * 1024 ))
113 #endif
114 
115 //*****************************************************************************
116 //
117 // Globals
118 //
119 //*****************************************************************************
120 
121 // temporary stack (in case the HF was caused by invalid stack)
122 #if defined(AM_LOCAL_STACK)
123 uint8_t gFaultStack[512];    // needs ~320 bytes (+7 for 8-byte alignment)
124 #endif
125 
126 //*****************************************************************************
127 //
128 // Data structures
129 //
130 //*****************************************************************************
131 //
132 // Define a structure for local storage in am_util_faultisr_collect_data().
133 // Set structure alignment to 1 byte to minimize storage requirements.
134 //
135 #pragma pack(1)
136 typedef struct
137 {
138     //
139     // Stacked registers
140     //
141     volatile uint32_t u32R0;
142     volatile uint32_t u32R1;
143     volatile uint32_t u32R2;
144     volatile uint32_t u32R3;
145     volatile uint32_t u32R12;
146     volatile uint32_t u32LR;
147     volatile uint32_t u32PC;
148     volatile uint32_t u32PSR;
149 
150     //
151     // Other data
152     //
153     volatile uint32_t u32FaultAddr;
154     volatile uint32_t u32BFAR;
155     volatile uint32_t u32CFSR;
156     volatile uint8_t  u8MMSR;
157     volatile uint8_t  u8BFSR;
158     volatile uint16_t u16UFSR;
159 
160 } am_fault_t;
161 
162 
163 //
164 // Restore the default structure alignment
165 //
166 #pragma pack()
167 
168 //*****************************************************************************
169 //
170 // Prototypes
171 //
172 //*****************************************************************************
173 void am_util_faultisr_collect_data(uint32_t u32IsrSP);
174 bool am_valid_sp(uint32_t u32IsrSP);
175 
176 //
177 // Prototype for printf, if used.
178 //
179 extern uint32_t am_util_stdio_printf(char *pui8Fmt, ...);
180 
181 
182 //*****************************************************************************
183 //
184 // getStackedReg() will retrieve a specified register value, as it was stacked
185 // by the processor after the fault, from the stack.
186 //
187 // The registers are stacked in the following order:
188 //  R0, R1, R2, R3, R12, LR, PC, PSR.
189 // To get R0 from the stack, call getStackedReg(0), r1 is getStackedReg(1)...
190 //
191 //*****************************************************************************
192 #if (defined (__ARMCC_VERSION)) && (__ARMCC_VERSION < 6000000)
193 __asm uint32_t
194 #if AM_CMSIS_REGS
HardFault_Handler(void)195 HardFault_Handler(void)
196 #else // AM_CMSIS_REGS
197 am_fault_isr(void)
198 #endif // AM_CMSIS_REGS
199 
200 #if defined(AM_LOCAL_STACK)
201 {
202     PRESERVE8
203     import  am_util_faultisr_collect_data
204     import  gFaultStack
205     tst     lr, #4                        // Check if we should use MSP or PSP
206     ite     eq                            // Instrs executed when: eq,ne
207     mrseq   r0, msp                       // t: bit2=0 indicating MSP stack
208     mrsne   r0, psp                       // e: bit2=1 indicating PSP stack
209     ldr     r1, =gFaultStack              // get address of the base of the temp_stack
210     add     r1, r1, #512                  // address of the top of the stack.
211     bic     r1, #3                        // make sure the new stack is 8-byte aligned
212     mov     sp, r1                        // move the new stack address to the SP
213     b       am_util_faultisr_collect_data // no return - simple branch to get fault info
214     nop                                   // Avoid compiler warning about padding 2 bytes
215 }
216 #else // no local stack
217 {
218     PRESERVE8
219     import  am_util_faultisr_collect_data
220     tst     lr, #4                        // Check if we should use MSP or PSP
221     ite     eq                            // Instrs executed when: eq,ne
222     mrseq   r0, msp                       // t: bit2=0 indicating MSP stack
223     mrsne   r0, psp                       // e: bit2=1 indicating PSP stack
224     b       am_util_faultisr_collect_data // no return - simple branch to get fault info
225 }
226 #endif
227 
228 __asm uint32_t
getStackedReg(uint32_t regnum,uint32_t u32SP)229 getStackedReg(uint32_t regnum, uint32_t u32SP)
230 {
231     lsls    r0, r0, #2
232     adds    r0, r0, r1
233     ldr     r0, [r0]
234     bx      lr
235 }
236 
237 #elif (defined (__ARMCC_VERSION)) && (__ARMCC_VERSION > 6000000)
238 uint32_t __attribute__((naked))
239 #if AM_CMSIS_REGS
HardFault_Handler(void)240 HardFault_Handler(void)
241 #else // AM_CMSIS_REGS
242 am_fault_isr(void)
243 #endif // AM_CMSIS_REGS
244 {
245     __asm("    tst    lr, #4\n"                          // Check if we should use MSP or PSP
246           "    ite    eq\n"                              // Instrs executed when: eq,ne
247           "    mrseq  r0, msp\n"                         // t: bit2=0 indicating MSP stack
248           "    mrsne  r0, psp\n");                       // e: bit2=1 indicating PSP stack
249 #if defined(AM_LOCAL_STACK)
250     __asm("    ldr    r1, =gFaultStack\n"                // get address of the base of the temp_stack
251           "    add    r1, r1, #512\n"                    // address of the top of the stack.
252           "    bic    r1, #3\n"                          // make sure the new stack is 8-byte aligned
253           "    mov    sp,r1\n");                         // move the new stack address to the SP
254 #endif
255     __asm("    b      am_util_faultisr_collect_data\n"); // no return - simple branch to get fault info
256 }
257 
258 uint32_t __attribute__((naked))
getStackedReg(uint32_t regnum,uint32_t u32SP)259 getStackedReg(uint32_t regnum, uint32_t u32SP)
260 {
261     __asm("    lsls    r0, r0, #2");
262     __asm("    adds    r0, r1");
263     __asm("    ldr     r0, [r0]");
264     __asm("    bx      lr");
265 }
266 #elif defined(__GNUC_STDC_INLINE__)
267 uint32_t __attribute__((naked))
268 #if AM_CMSIS_REGS
HardFault_Handler(void)269 HardFault_Handler(void)
270 #else // AM_CMSIS_REGS
271 am_fault_isr(void)
272 #endif // AM_CMSIS_REGS
273 {
274      __asm("    tst    lr, #4\n"                          // Check if we should use MSP or PSP
275           "    ite    eq\n"                              // Instrs executed when: eq,ne
276           "    mrseq  r0, msp\n"                         // t: bit2=0 indicating MSP stack
277           "    mrsne  r0, psp\n");                       // e: bit2=1 indicating PSP stack
278 #if defined(AM_LOCAL_STACK)
279     __asm("    ldr    r1, =gFaultStack\n"                // get address of the base of the temp_stack
280           "    add    r1, r1, #512\n"                    // address of the top of the stack.
281           "    bic    r1, #3\n"                          // make sure the new stack is 8-byte aligned
282           "    mov    sp,r1\n");                         // move the new stack address to the SP
283 #endif
284     __asm("    b      am_util_faultisr_collect_data\n"); // no return - simple branch to get fault info
285 }
286 
287 uint32_t __attribute__((naked))
getStackedReg(uint32_t regnum,uint32_t u32SP)288 getStackedReg(uint32_t regnum, uint32_t u32SP)
289 {
290     __asm("    lsls    r0, r0, #2");
291     __asm("    adds    r0, r1");
292     __asm("    ldr     r0, [r0]");
293     __asm("    bx      lr");
294 }
295 #elif defined(__IAR_SYSTEMS_ICC__)
296 #pragma diag_suppress = Pe940   // Suppress IAR compiler warning about missing
297                                 // return statement on a non-void function
298 __stackless uint32_t
299 #if AM_CMSIS_REGS
HardFault_Handler(void)300 HardFault_Handler(void)
301 #else // AM_CMSIS_REGS
302 am_fault_isr(void)
303 #endif // AM_CMSIS_REGS
304 {
305     __asm("    tst    lr, #4\n"                          // Check if we should use MSP or PSP
306           "    ite    eq\n"                              // Instrs executed when: eq,ne
307           "    mrseq  r0, msp\n"                         // t: bit2=0 indicating MSP stack
308           "    mrsne  r0, psp\n");                       // e: bit2=1 indicating PSP stack
309 #if defined(AM_LOCAL_STACK)
310     __asm("    ldr    r1, =gFaultStack\n"                // get address of the base of the temp_stack
311           "    add    r1, r1, #512\n"                    // address of the top of the stack.
312           "    bic    r1, #3\n"                          // make sure the new stack is 8-byte aligned
313           "    mov    sp,r1\n");                         // move the new stack address to the SP
314 #endif
315     __asm("    b      am_util_faultisr_collect_data\n"); // no return - simple branch to get fault info
316 }
317 
318 __stackless uint32_t
getStackedReg(uint32_t regnum,uint32_t u32SP)319 getStackedReg(uint32_t regnum, uint32_t u32SP)
320 {
321     __asm("     lsls    r0, r0, #2");
322     __asm("     adds    r0, r0, r1");
323     __asm("     ldr     r0, [r0]");
324     __asm("     bx      lr");
325 }
326 #pragma diag_default = Pe940    // Restore IAR compiler warning
327 #endif
328 
329 //*****************************************************************************
330 //
331 // am_util_faultisr_collect_data(uint32_t u32IsrSP);
332 //
333 // This function is intended to be called by HardFault_Handler(), called
334 // when the processor receives a hard fault interrupt.  This part of the
335 // handler parses through the various fault codes and saves them into a data
336 // structure so they can be readily examined by the user in the debugger.
337 //
338 // The input u32IsrSP is expected to be the value of the stack pointer when
339 // HardFault_Handler() was called.
340 //
341 //*****************************************************************************
342 void
am_util_faultisr_collect_data(uint32_t u32IsrSP)343 am_util_faultisr_collect_data(uint32_t u32IsrSP)
344 {
345     volatile am_fault_t sFaultData;
346 #if defined(AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4P) || defined(AM_PART_APOLLO4L)
347     am_hal_fault_status_t  sHalFaultData = {0};
348 #elif defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P) || defined(AM_PART_APOLLO2) || defined(AM_PART_APOLLO)
349     am_hal_mcuctrl_fault_t sHalFaultData = {0};
350 #endif // if defined(AM_PART_APOLLO4X)
351 
352     uint32_t u32Mask = 0;
353 
354     //
355     // Following is a brief overview of fault information provided by the M4.
356     // More details can be found in the Cortex M4 User Guide.
357     //
358     // CFSR (Configurable Fault Status Reg) contains MMSR, BFSR, and UFSR:
359     //   7:0    MMSR (MemManage)
360     //          [0] IACCVIOL    Instr fetch from a location that does not
361     //                          permit execution.
362     //          [1] DACCVIOL    Data access violation flag. MMAR contains
363     //                          address of the attempted access.
364     //          [2] Reserved
365     //          [3] MUNSTKERR   MemMange fault on unstacking for a return
366     //                          from exception.
367     //          [4] MSTKERR     MemMange fault on stacking for exception
368     //                          entry.
369     //          [5] MLSPERR     MemMange fault during FP lazy state
370     //                          preservation.
371     //          [6] Reserved
372     //          [7] MMARVALID   MemManage Fault Addr Reg (MMFAR) valid flag.
373     //  15:8    BusFault
374     //          [0] IBUSERR     If set, instruction bus error.
375     //          [1] PRECISERR   Data bus error. Stacked PC points to instr
376     //                          that caused the fault.
377     //          [2] IMPRECISERR Data bus error, but stacked return addr is not
378     //                          related to the instr that caused the error and
379     //                          BFAR is not valid.
380     //          [3] UNSTKERR    Bus fault on unstacking for a return from
381     //                          exception.
382     //          [4] STKERR      Bus fault on stacking for exception entry.
383     //          [5] LSPERR      Bus fault during FP lazy state preservation.
384     //          [6] Reserved
385     //          [7] BFARVALID   BFAR valid.
386     //  31:16   UFSR (UsageFault)
387     //          [0] UNDEFINSTR  Undefined instruction.
388     //          [1] INVSTATE    Invalid state.
389     //          [2] INVPC       Invalid PC load.
390     //          [3] NOCP        No coprocessor.
391     //        [7:4] Reserved
392     //          [8] UNALIGNED   Unaligned access.
393     //          [9] DIVBYZERO   Divide by zero.
394     //      [15:10] Reserved
395     //
396 
397     //
398     // u32Mask is used for 2 things: 1) in the print loop, 2) as a spot to set
399     // a breakpoint at the end of the routine.  If the printing is not used,
400     // we'll get a compiler warning; so to avoid that warning, we'll use it
401     // in a dummy assignment here.
402     //
403     sFaultData.u32CFSR = u32Mask;       // Avoid compiler warning
404     sFaultData.u32CFSR = AM_REGVAL(AM_REG_SYSCTRL_CFSR_O);
405     sFaultData.u8MMSR  = (sFaultData.u32CFSR >> 0)  & 0xff;
406     sFaultData.u8BFSR  = (sFaultData.u32CFSR >> 8)  & 0xff;
407     sFaultData.u16UFSR = (sFaultData.u32CFSR >> 16) & 0xffff;
408 
409     //
410     // The address of the location that caused the fault.  e.g. if accessing an
411     // invalid data location caused the fault, that address will appear here.
412     //
413     sFaultData.u32BFAR = AM_REGVAL(AM_REG_SYSCTRL_BFAR_O);
414 
415     // make sure that the SP points to a valid address (so that accessing the stack frame doesn't cause another fault).
416     if (am_valid_sp(u32IsrSP))
417     {
418         //
419         // The address of the instruction that caused the fault is the stacked PC
420         // if BFSR bit1 is set.
421         //
422         sFaultData.u32FaultAddr = (sFaultData.u8BFSR & 0x02) ? getStackedReg(6, u32IsrSP) : 0xffffffff;
423 
424         //
425         // Get the stacked registers.
426         // Note - the address of the instruction that caused the fault is u32PC.
427         //
428         sFaultData.u32R0  = getStackedReg(0, u32IsrSP);
429         sFaultData.u32R1  = getStackedReg(1, u32IsrSP);
430         sFaultData.u32R2  = getStackedReg(2, u32IsrSP);
431         sFaultData.u32R3  = getStackedReg(3, u32IsrSP);
432         sFaultData.u32R12 = getStackedReg(4, u32IsrSP);
433         sFaultData.u32LR  = getStackedReg(5, u32IsrSP);
434         sFaultData.u32PC  = getStackedReg(6, u32IsrSP);
435         sFaultData.u32PSR = getStackedReg(7, u32IsrSP);
436     }
437     //
438     // Use the HAL MCUCTRL functions to read the fault data.
439     //
440 #if defined(AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4P) || defined(AM_PART_APOLLO4L)
441     am_hal_fault_status_get(&sHalFaultData);
442 #elif defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
443     am_hal_mcuctrl_info_get(AM_HAL_MCUCTRL_INFO_FAULT_STATUS, &sHalFaultData);
444 #elif defined(AM_PART_APOLLO2) || defined(AM_PART_APOLLO)
445     am_hal_mcuctrl_fault_status(&sHalFaultData);
446 #endif
447 
448 #ifdef AM_UTIL_FAULTISR_PRINT
449     //
450     // If printf has previously been initialized in the application, we should
451     // be able to print out the fault information.
452     //
453     am_util_stdio_printf("** Hard Fault Occurred:\n\n");
454     if (!am_valid_sp(u32IsrSP))
455     {
456         am_util_stdio_printf("    Invalid SP when Hard Fault occured: 0x%08X (no Stacked data)\n\n");
457     }
458     else
459     {
460         am_util_stdio_printf("Hard Fault stacked data:\n");
461         am_util_stdio_printf("    R0  = 0x%08X\n", sFaultData.u32R0);
462         am_util_stdio_printf("    R1  = 0x%08X\n", sFaultData.u32R1);
463         am_util_stdio_printf("    R2  = 0x%08X\n", sFaultData.u32R2);
464         am_util_stdio_printf("    R3  = 0x%08X\n", sFaultData.u32R3);
465         am_util_stdio_printf("    R12 = 0x%08X\n", sFaultData.u32R12);
466         am_util_stdio_printf("    LR  = 0x%08X\n", sFaultData.u32LR);
467         am_util_stdio_printf("    PC  = 0x%08X\n", sFaultData.u32PC);
468         am_util_stdio_printf("    PSR = 0x%08X\n\n", sFaultData.u32PSR);
469     }
470     am_util_stdio_printf("Other Hard Fault data:\n");
471     am_util_stdio_printf("    Fault address = 0x%08X\n", sFaultData.u32FaultAddr);
472     am_util_stdio_printf("    BFAR (Bus Fault Addr Reg) = 0x%08X\n", sFaultData.u32BFAR);
473     am_util_stdio_printf("    MMSR (Mem Mgmt Fault Status Reg) = 0x%02X\n", sFaultData.u8MMSR);
474     am_util_stdio_printf("    UFSR (Usage Fault Status Reg) = 0x%04X\n", sFaultData.u16UFSR);
475     am_util_stdio_printf("    BFSR (Bus Fault Status Reg) = 0x%02X\n", sFaultData.u8BFSR);
476     //
477     // Print out any bits set in the BFSR.
478     //
479     u32Mask = 0x80;
480     while (u32Mask)
481     {
482         switch (sFaultData.u8BFSR & u32Mask)
483         {
484             case 0x80:
485                 am_util_stdio_printf("        BFSR bit7: BFARVALID\n");
486                 break;
487             case 0x40:
488                 am_util_stdio_printf("        BFSR bit6: RESERVED\n");
489                 break;
490             case 0x20:
491                 am_util_stdio_printf("        BFSR bit5: LSPERR\n");
492                 break;
493             case 0x10:
494                 am_util_stdio_printf("        BFSR bit4: STKERR\n");
495                 break;
496             case 0x08:
497                 am_util_stdio_printf("        BFSR bit3: UNSTKERR\n");
498                 break;
499             case 0x04:
500                 am_util_stdio_printf("        BFSR bit2: IMPRECISERR\n");
501                 break;
502             case 0x02:
503                 am_util_stdio_printf("        BFSR bit1: PRECISEERR\n");
504                 break;
505             case 0x01:
506                 am_util_stdio_printf("        BFSR bit0: IBUSERR\n");
507                 break;
508             default:
509                 break;
510         }
511         u32Mask >>= 1;
512     }
513 
514     //
515     // Print out any Apollo* Internal fault information - if any
516     //
517     if (sHalFaultData.bICODE || sHalFaultData.bDCODE || sHalFaultData.bSYS)
518     {
519         am_util_stdio_printf("\nMCU Fault data:\n");
520     }
521     if (sHalFaultData.bICODE)
522     {
523         am_util_stdio_printf("    ICODE Fault Address: 0x%08X\n", sHalFaultData.ui32ICODE);
524     }
525     if (sHalFaultData.bDCODE)
526     {
527         am_util_stdio_printf("    DCODE Fault Address: 0x%08X\n", sHalFaultData.ui32DCODE);
528     }
529     if (sHalFaultData.bSYS)
530     {
531         am_util_stdio_printf("    SYS Fault Address: 0x%08X\n", sHalFaultData.ui32SYS);
532     }
533     //
534     // Spin in an infinite loop.
535     // We need to spin here inside the function so that we have access to
536     // local data, i.e. sFaultData.
537     //
538     am_util_stdio_printf("\n\nDone with output. Entering infinite loop.\n\n");
539 
540 #endif  // AM_UTIL_FAULTISR_PRINT
541 
542     u32Mask = 0;
543 
544     while (1)  {  };   // spin forever
545 }
546 
547 //*****************************************************************************
548 //
549 // am_valid_sp(uint32_t u32IsrSP);
550 //
551 // This function does a range on the SP to make sure it appears to be valid
552 //
553 // The input param u32IsrSP is expected to be the value of the stack pointer in
554 // use when the hardfault occured.
555 //
556 //*****************************************************************************
557 bool
am_valid_sp(uint32_t u32IsrSP)558 am_valid_sp(uint32_t u32IsrSP)
559 {
560     return ( (u32IsrSP >= AM_SP_LOW) && (u32IsrSP < AM_SP_HIGH) ) ? true : false;
561 }
562 //*****************************************************************************
563 
564 
565 //*****************************************************************************
566 //
567 // End Doxygen group.
568 //! @}
569 //
570 //*****************************************************************************
571