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, define AM_HF_NO_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 or output.
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 #define 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) 2024, 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 stable-c1f95ddf60 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_HF_NO_LOCAL_STACK      // when defined use existing MSP (no local stack)
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 #define AM_NUM_STACK_RANGES 1
99 
100 #if defined(AM_PART_APOLLO5A) || defined(AM_PART_APOLLO5B)
101   #undef  AM_NUM_STACK_RANGES
102   #define AM_NUM_STACK_RANGES  2
103   #define AM_SP_LOW    ITCM_BASEADDR
104   #define AM_SP_HIGH   (ITCM_BASEADDR + ITCM_MAX_SIZE)
105   #define AM_SP_LOW2   DTCM_BASEADDR
106   #define AM_SP_HIGH2  (DTCM_BASEADDR + DTCM_MAX_SIZE + SSRAM_MAX_SIZE)
107 #elif defined(AM_PART_APOLLO4_API)
108   #define AM_SP_LOW    SRAM_BASEADDR
109   #define AM_SP_HIGH   (SRAM_BASEADDR + RAM_TOTAL_SIZE)
110 #elif defined(AM_PART_APOLLO3P)
111   #define AM_SP_LOW    SRAM_BASEADDR
112   #define AM_SP_HIGH   (SRAM_BASEADDR + ( 768 * 1024 ))
113 #elif defined(AM_PART_APOLLO3)
114   #define AM_SP_LOW    SRAM_BASEADDR
115   #define AM_SP_HIGH   (SRAM_BASEADDR + ( 384 * 1024 ))
116 #elif defined(AM_PART_APOLLO2)
117   #define AM_SP_LOW    SRAM_BASEADDR
118   #define AM_SP_HIGH   (SRAM_BASEADDR + ( 256 * 1024 ))
119 #elif defined(AM_PART_APOLLO)
120   #define AM_SP_LOW    SRAM_BASEADDR
121   #define AM_SP_HIGH   (SRAM_BASEADDR + ( 64 * 1024 ))
122 #endif
123 
124 //*****************************************************************************
125 //
126 // Globals
127 //
128 //*****************************************************************************
129 
130 // temporary stack (in case the HF was caused by invalid stack)
131 #if !defined(AM_HF_NO_LOCAL_STACK)
132 uint64_t gFaultStack[64];    // needs ~320 bytes (8-byte alignment)
133 #endif
134 
135 //*****************************************************************************
136 //
137 // Data structures
138 //
139 //*****************************************************************************
140 //
141 // Define a structure for local storage in am_util_faultisr_collect_data().
142 // Set structure alignment to 1 byte to minimize storage requirements.
143 //
144 #pragma pack(1)
145 typedef struct
146 {
147     //
148     // Stacked registers
149     //
150     volatile uint32_t u32R0;
151     volatile uint32_t u32R1;
152     volatile uint32_t u32R2;
153     volatile uint32_t u32R3;
154     volatile uint32_t u32R12;
155     volatile uint32_t u32LR;
156     volatile uint32_t u32PC;
157     volatile uint32_t u32PSR;
158 
159     //
160     // Other data
161     //
162     volatile uint32_t u32FaultAddr;
163     volatile uint32_t u32BFAR;
164     volatile uint32_t u32CFSR;
165     volatile uint8_t  u8MMSR;
166     volatile uint8_t  u8BFSR;
167     volatile uint16_t u16UFSR;
168 
169 } am_fault_t;
170 
171 //
172 // Restore the default structure alignment
173 //
174 #pragma pack()
175 
176 //*****************************************************************************
177 //
178 // Prototypes
179 //
180 //*****************************************************************************
181 uint32_t am_getStackedReg(uint32_t regnum, uint32_t *u32SP);
182 void am_util_faultisr_collect_data(uint32_t *u32IsrSP);
183 bool am_valid_sp(uint32_t *u32IsrSP);
184 
185 //
186 // Prototype for printf, if used.
187 //
188 extern uint32_t am_util_stdio_printf(char *pui8Fmt, ...);
189 
190 //*****************************************************************************
191 //
192 // HardFault_Handler() gets control when a hard fault occurs.  it pass the previous
193 // active stack pointer (MSP or PSP) in R0 to the function am_util_faultisr_collect_data()
194 // which will never return.   If AM_HF_NO_LOCAL_STACK is not defined, it sets the MSP to
195 // the local stack, and for M55 devices sets the MSPLIM register.
196 //
197 //*****************************************************************************
198 
199 // for GCC, ARM6 and IAR, all use the same inline assembly syntax
200 #if defined(__GNUC_STDC_INLINE__) || defined(__IAR_SYSTEMS_ICC__) || ((defined(__ARMCC_VERSION)) && (__ARMCC_VERSION > 6000000))
201 #if defined(__IAR_SYSTEMS_ICC__)
202 #pragma diag_suppress = Pe940   // Suppress IAR compiler warning about missing
203                                 // return statement on a non-void function
204 __stackless uint32_t
205 #else // GCC or ARM6
206 uint32_t __attribute__((naked))
207 #endif
HardFault_Handler(void)208 HardFault_Handler(void)
209 {
210     __asm("    tst    lr, #4\n"                          // Check if we should use MSP or PSP
211           "    ite    eq\n"                              // Instrs executed when: eq,ne
212           "    mrseq  r0, msp\n"                         // t: bit2=0 indicating MSP stack
213           "    mrsne  r0, psp\n");                       // e: bit2=1 indicating PSP stack
214 #if !defined(AM_HF_NO_LOCAL_STACK)
215     __asm("    ldr    r1, =gFaultStack\n");              // get address of the base of the temp_stack
216 #if defined(AM_PART_APOLLO5A) || defined(AM_PART_APOLLO5B)
217     __asm("    MSR msplim, r1\n");                       // for Apollo5 (M55) set MSP stack limit register
218 #endif
219     __asm("    add    r1, r1, #512\n"                    // address of the top of the stack.
220           "    bic    r1, #3\n"                          // make sure the new stack is 8-byte aligned
221           "    mov    sp, r1\n");                        // move the new stack address to the SP
222 #endif  // !defined(AM_HF_NO_LOCAL_STACK)
223     __asm("    b      am_util_faultisr_collect_data\n"); // no return - simple branch to get fault info
224 }
225 #endif // for GCC, ARM6 and IAR,
226 
227 // for ARM5 compiler
228 #if (defined (__ARMCC_VERSION)) && (__ARMCC_VERSION < 6000000)
229 __asm uint32_t
HardFault_Handler(void)230 HardFault_Handler(void)
231 
232 #if !defined(AM_HF_NO_LOCAL_STACK)    // Apollo5 does not support ARM5 - can not be used for M55
233 {
234     PRESERVE8
235     import  am_util_faultisr_collect_data
236     import  gFaultStack
237     tst     lr, #4                        // Check if we should use MSP or PSP
238     ite     eq                            // Instrs executed when: eq,ne
239     mrseq   r0, msp                       // t: bit2=0 indicating MSP stack
240     mrsne   r0, psp                       // e: bit2=1 indicating PSP stack
241     ldr     r1, =gFaultStack              // get address of the base of the temp_stack
242     add     r1, r1, #512                  // address of the top of the stack.
243     bic     r1, #3                        // make sure the new stack is 8-byte aligned
244     mov     sp, r1                        // move the new stack address to the SP
245     b       am_util_faultisr_collect_data // no return - simple branch to get fault info
246     movs    r0, r0                        // fill instruction for 4-byte alignment - not executed
247 }
248 #else // no local stack
249 {
250     PRESERVE8
251     import  am_util_faultisr_collect_data
252     tst     lr, #4                        // Check if we should use MSP or PSP
253     ite     eq                            // Instrs executed when: eq,ne
254     mrseq   r0, msp                       // t: bit2=0 indicating MSP stack
255     mrsne   r0, psp                       // e: bit2=1 indicating PSP stack
256     b       am_util_faultisr_collect_data // no return - simple branch to get fault info
257     movs    r0, r0                        // fill instruction for 4-byte alignment - not executed
258 }
259 #endif  // !defined(AM_HF_NO_LOCAL_STACK)
260 
261 #endif  // ARM5 Compiler
262 
263 //*****************************************************************************
264 //
265 // am_getStackedReg() will retrieve a specified register value, as it was stacked
266 // by the processor after the fault, from the stack.
267 //
268 // The registers are stacked in the following order:
269 //  R0, R1, R2, R3, R12, LR, PC, PSR.
270 //
271 // To get R0 from the stack, call getStackedReg(0), r1 is getStackedReg(1)...
272 //
273 //*****************************************************************************
274 uint32_t
am_getStackedReg(uint32_t regnum,uint32_t * u32SP)275 am_getStackedReg(uint32_t regnum, uint32_t *u32SP)
276 {
277     return (u32SP[regnum]);
278 }
279 
280 //*****************************************************************************
281 //
282 // am_util_faultisr_collect_data(uint32_t u32IsrSP);
283 //
284 // This function is intended to be called by HardFault_Handler(), called
285 // when the processor receives a hard fault interrupt.  This part of the
286 // handler parses through the various fault codes and saves them into a data
287 // structure so they can be readily examined by the user in the debugger or
288 // output to the stdout (SWO).  This function does not return to the caller.
289 //
290 // The input u32IsrSP is expected to be the value of the stack pointer when
291 // HardFault_Handler() was called.
292 //
293 //*****************************************************************************
294 void
am_util_faultisr_collect_data(uint32_t * u32IsrSP)295 am_util_faultisr_collect_data(uint32_t *u32IsrSP)
296 {
297     volatile am_fault_t sFaultData;
298 
299 #if defined(AM_PART_APOLLO4_API)
300     am_hal_fault_status_t  sHalFaultData = {0};
301 #elif defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P) || defined(AM_PART_APOLLO2) || defined(AM_PART_APOLLO)
302     am_hal_mcuctrl_fault_t sHalFaultData = {0};
303 #endif
304 
305     uint32_t u32Mask = 0;
306 
307     //
308     // Following is a brief overview of fault information provided by the M4.
309     // More details can be found in the Cortex M4/M55 User Guide.
310     //
311     // CFSR (Configurable Fault Status Reg) contains MMSR, BFSR, and UFSR:
312     //   7:0    MMSR (MemManage)
313     //          [0] IACCVIOL    Instr fetch from a location that does not
314     //                          permit execution.
315     //          [1] DACCVIOL    Data access violation flag. MMAR contains
316     //                          address of the attempted access.
317     //          [2] Reserved
318     //          [3] MUNSTKERR   MemMange fault on unstacking for a return
319     //                          from exception.
320     //          [4] MSTKERR     MemMange fault on stacking for exception
321     //                          entry.
322     //          [5] MLSPERR     MemMange fault during FP lazy state
323     //                          preservation.
324     //          [6] Reserved
325     //          [7] MMARVALID   MemManage Fault Addr Reg (MMFAR) valid flag.
326     //  15:8    BusFault
327     //          [0] IBUSERR     If set, instruction bus error.
328     //          [1] PRECISERR   Data bus error. Stacked PC points to instr
329     //                          that caused the fault.
330     //          [2] IMPRECISERR Data bus error, but stacked return addr is not
331     //                          related to the instr that caused the error and
332     //                          BFAR is not valid.
333     //          [3] UNSTKERR    Bus fault on unstacking for a return from
334     //                          exception.
335     //          [4] STKERR      Bus fault on stacking for exception entry.
336     //          [5] LSPERR      Bus fault during FP lazy state preservation.
337     //          [6] Reserved
338     //          [7] BFARVALID   BFAR valid.
339     //  31:16   UFSR (UsageFault)
340     //          [0] UNDEFINSTR  Undefined instruction.
341     //          [1] INVSTATE    Invalid state.
342     //          [2] INVPC       Invalid PC load.
343     //          [3] NOCP        No coprocessor.
344     //        [7:4] Reserved
345     //          [8] UNALIGNED   Unaligned access.
346     //          [9] DIVBYZERO   Divide by zero.
347     //      [15:10] Reserved
348     //
349 
350     //
351     // u32Mask is used for 2 things: 1) in the print loop, 2) as a spot to set
352     // a breakpoint at the end of the routine.  If the printing is not used,
353     // we'll get a compiler warning; so to avoid that warning, we'll use it
354     // in a dummy assignment here.
355     //
356     sFaultData.u32CFSR = u32Mask;       // Avoid compiler warning
357     sFaultData.u32CFSR = AM_REGVAL(AM_REG_SYSCTRL_CFSR_O);
358     sFaultData.u8MMSR  = (sFaultData.u32CFSR >> 0)  & 0xff;
359     sFaultData.u8BFSR  = (sFaultData.u32CFSR >> 8)  & 0xff;
360     sFaultData.u16UFSR = (sFaultData.u32CFSR >> 16) & 0xffff;
361 
362     //
363     // The address of the location that caused the fault.  e.g. if accessing an
364     // invalid data location caused the fault, that address will appear here.
365     //
366     sFaultData.u32BFAR = AM_REGVAL(AM_REG_SYSCTRL_BFAR_O);
367 
368     // make sure that the SP points to a valid address (so that accessing the stack frame doesn't cause another fault).
369     if (am_valid_sp(u32IsrSP))
370     {
371         //
372         // The address of the instruction that caused the fault is the stacked PC
373         // if BFSR bit1 is set.
374         //
375         sFaultData.u32FaultAddr = (sFaultData.u8BFSR & 0x02) ? am_getStackedReg(6, u32IsrSP) : 0xffffffff;
376 
377         //
378         // Get the stacked registers.
379         // Note - the address of the instruction that caused the fault is u32PC.
380         //
381         sFaultData.u32R0  = am_getStackedReg(0, u32IsrSP);
382         sFaultData.u32R1  = am_getStackedReg(1, u32IsrSP);
383         sFaultData.u32R2  = am_getStackedReg(2, u32IsrSP);
384         sFaultData.u32R3  = am_getStackedReg(3, u32IsrSP);
385         sFaultData.u32R12 = am_getStackedReg(4, u32IsrSP);
386         sFaultData.u32LR  = am_getStackedReg(5, u32IsrSP);
387         sFaultData.u32PC  = am_getStackedReg(6, u32IsrSP);
388         sFaultData.u32PSR = am_getStackedReg(7, u32IsrSP);
389     }
390     //
391     // Use the HAL MCUCTRL functions to read the fault data.
392     //
393 
394 #if defined(AM_PART_APOLLO4_API)
395     am_hal_fault_status_get(&sHalFaultData);
396 #elif defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
397     am_hal_mcuctrl_info_get(AM_HAL_MCUCTRL_INFO_FAULT_STATUS, &sHalFaultData);
398 #elif defined(AM_PART_APOLLO2) || defined(AM_PART_APOLLO)
399     am_hal_mcuctrl_fault_status(&sHalFaultData);
400 #endif
401 
402 #ifdef AM_UTIL_FAULTISR_PRINT
403     //
404     // If printf has previously been initialized in the application, we should
405     // be able to print out the fault information.
406     //
407     am_util_stdio_printf("** Hard Fault Occurred:\n\n");
408     if (!am_valid_sp(u32IsrSP))
409     {
410         am_util_stdio_printf("    Invalid SP when Hard Fault occured: 0x%08X (no Stacked data)\n\n");
411     }
412     else
413     {
414         am_util_stdio_printf("Hard Fault stacked data:\n");
415         am_util_stdio_printf("    R0  = 0x%08X\n", sFaultData.u32R0);
416         am_util_stdio_printf("    R1  = 0x%08X\n", sFaultData.u32R1);
417         am_util_stdio_printf("    R2  = 0x%08X\n", sFaultData.u32R2);
418         am_util_stdio_printf("    R3  = 0x%08X\n", sFaultData.u32R3);
419         am_util_stdio_printf("    R12 = 0x%08X\n", sFaultData.u32R12);
420         am_util_stdio_printf("    LR  = 0x%08X\n", sFaultData.u32LR);
421         am_util_stdio_printf("    PC  = 0x%08X\n", sFaultData.u32PC);
422         am_util_stdio_printf("    PSR = 0x%08X\n\n", sFaultData.u32PSR);
423     }
424     am_util_stdio_printf("Other Hard Fault data:\n");
425     am_util_stdio_printf("    Fault address = 0x%08X\n", sFaultData.u32FaultAddr);
426     am_util_stdio_printf("    BFAR (Bus Fault Addr Reg) = 0x%08X\n", sFaultData.u32BFAR);
427     am_util_stdio_printf("    MMSR (Mem Mgmt Fault Status Reg) = 0x%02X\n", sFaultData.u8MMSR);
428     am_util_stdio_printf("    UFSR (Usage Fault Status Reg) = 0x%04X\n", sFaultData.u16UFSR);
429     am_util_stdio_printf("    BFSR (Bus Fault Status Reg) = 0x%02X\n", sFaultData.u8BFSR);
430     //
431     // Print out any bits set in the BFSR.
432     //
433     u32Mask = 0x80;
434     while (u32Mask)
435     {
436         switch (sFaultData.u8BFSR & u32Mask)
437         {
438             case 0x80:
439                 am_util_stdio_printf("        BFSR bit7: BFARVALID\n");
440                 break;
441             case 0x40:
442                 am_util_stdio_printf("        BFSR bit6: RESERVED\n");
443                 break;
444             case 0x20:
445                 am_util_stdio_printf("        BFSR bit5: LSPERR\n");
446                 break;
447             case 0x10:
448                 am_util_stdio_printf("        BFSR bit4: STKERR\n");
449                 break;
450             case 0x08:
451                 am_util_stdio_printf("        BFSR bit3: UNSTKERR\n");
452                 break;
453             case 0x04:
454                 am_util_stdio_printf("        BFSR bit2: IMPRECISERR\n");
455                 break;
456             case 0x02:
457                 am_util_stdio_printf("        BFSR bit1: PRECISEERR\n");
458                 break;
459             case 0x01:
460                 am_util_stdio_printf("        BFSR bit0: IBUSERR\n");
461                 break;
462             default:
463                 break;
464         }
465         u32Mask >>= 1;
466     }
467 
468 #if !defined(AM_PART_APOLLO5A) && !defined(AM_PART_APOLLO5B) // No CPU register block in Apollo5
469     //
470     // Print out any Apollo* Internal fault information - if any
471     //
472     if (sHalFaultData.bICODE || sHalFaultData.bDCODE || sHalFaultData.bSYS)
473     {
474         am_util_stdio_printf("\nMCU Fault data:\n");
475     }
476     if (sHalFaultData.bICODE)
477     {
478         am_util_stdio_printf("    ICODE Fault Address: 0x%08X\n", sHalFaultData.ui32ICODE);
479     }
480     if (sHalFaultData.bDCODE)
481     {
482         am_util_stdio_printf("    DCODE Fault Address: 0x%08X\n", sHalFaultData.ui32DCODE);
483     }
484     if (sHalFaultData.bSYS)
485     {
486         am_util_stdio_printf("    SYS Fault Address: 0x%08X\n", sHalFaultData.ui32SYS);
487     }
488 #endif  // !defined(AM_PART_APOLLO5A) && !defined(AM_PART_APOLLO5B)
489 
490     am_util_stdio_printf("\n\nDone with output. Entering infinite loop.\n\n");
491 
492     //
493     // Spin in an infinite loop.
494     // We need to spin here inside the function so that we have access to
495     // local data, i.e. sFaultData.
496     //
497 #endif  // AM_UTIL_FAULTISR_PRINT
498 
499     u32Mask = 0;
500 
501     while (1)  {  };   // spin forever
502 }
503 
504 //*****************************************************************************
505 //
506 // am_valid_sp(uint32_t u32IsrSP);
507 //
508 // This function does a range on the SP to make sure it appears to be valid
509 //
510 // The input param u32IsrSP is expected to be the value of the stack pointer in
511 // use when the hardfault occured.
512 //
513 //*****************************************************************************
514 bool
am_valid_sp(uint32_t * u32IsrSP)515 am_valid_sp(uint32_t *u32IsrSP)
516 {
517     // test for valid ranges
518     if ( ((uint32_t) u32IsrSP >= AM_SP_LOW) && ((uint32_t) u32IsrSP < AM_SP_HIGH) )
519     {
520         return true;
521     }
522 #if defined(AM_PART_APOLLO5A) || defined(AM_PART_APOLLO5B)
523     if ( ((uint32_t) u32IsrSP >= AM_SP_LOW2) && ((uint32_t) u32IsrSP < AM_SP_HIGH2) )
524     {
525         return true;
526     }
527 #endif
528     return false;  // not in any valid range
529 }
530 //*****************************************************************************
531 
532 
533 //*****************************************************************************
534 //
535 // End Doxygen group.
536 //! @}
537 //
538 //*****************************************************************************
539