EmbDev.net

Forum: ARM programming with GCC/GNU tools Cortex-M3 Fault Handlers

Author: Jerry Milner (jrmymllr)
Posted on:

I'm a little confused about writing a fault handler. I'd like to write
one to track down the occasional crash and display it on my LCD. I'd
guess that most, if not all the faults I see are because of invalid
memory address access (as in, trying to r/w a RAM address that doesn't
exist), so I believe that would be a Bus Fault. I've found there is a
BFAR register I could use, or by looking at the stack by way of a
register, PSP or MSP, although I don't know which one would be valid, or
what the disadvantages of each are.  It seems like there's some
conflicting info out there regarding this.

What I have really had trouble with is how to get a register into a C
variable. I've searched the web looking for answer, but nothing that
seems clear.  I though I could use

register int x asm("r1")

to get r1 into the variable "x", for example, but I'm not sure if this
will always work. What is a good way to do this in GCC? I've used my
debugger before to view the offending address, but I can't rely on that
(because random crashes never happen when it's connected).

Is there a set of C fault handler functions somewhere that do a good job
of this?
Author: 900ss D. (900ss)
Posted on:

Hi Jerry,

this is helpful for you I guess:
https://my.st.com/public/STe2ecommunities/mcu/List...

The guy who give the hint there has written a very good book, may be you
know it already: The definitive guide to the ARM Cortex-M3

I can suggest this book. It is really good.
Author: Marcus Harnisch (Company: Doulos) (mharnisch)
Posted on:

Jerry Milner wrote:
> so I believe that would be a Bus Fault.

Look at the fault status registers.

> I've found there is a BFAR register I could use, or by looking at the
> stack by way of a register, PSP or MSP, although I don't know which
> one would be valid, or what the disadvantages of each are.

What information are you looking for on the stack? The return address?
You will want to find out which stack pointer was used before entering
the exception (information in link register). Then look for the return
address at the appropriate offset.

> What I have really had trouble with is how to get a register into a C
> variable.

A decent compiler will do a good job allocating registers for variables.

>  I though I could use
>
> register int x asm("r1")
>
> to get r1 into the variable "x", for example, but I'm not sure if this
> will always work.

What this does is a hardwired mapping of the variable x to the register
r1.
While this is very useful for special purpose registers (e.g. PSP, MSP),
I don't recommend this for general use unless you can explain why
exactly you need this.

Regards
Marcus
http://www.doulos.com/arm/
Author: 900ss D. (900ss)
Posted on:

Marcus Harnisch wrote:
> What information are you looking for on the stack? The return address?
> You will want to find out which stack pointer was used before entering
> the exception (information in link register). Then look for the return
> address at the appropriate offset.

The return address and all the other status informations are found on
the stack at a exception.

The third posting of my link above gives an example (in 'C') how to get
these informations from the stack.
Author: Jerry Milner (jrmymllr)
Posted on:

Marcus, I wanted to map a register to a C variable (whichever way is
best) so I could have the fault information in a C variable, since often
asm is given to extract this information, then left in "r1" for example.

900ss, thanks for the link.  This looks like something I would like to
try, though they don't say it's for GCC.  I'm going to see if I can get
it to work.
Author: Marcus Harnisch (Company: Doulos) (mharnisch)
Posted on:

Hi Jerry

Jerry Milner wrote:
> Marcus, I wanted to map a register to a C variable (whichever way is
> best) so I could have the fault information in a C variable, since often
> asm is given to extract this information, then left in "r1" for example.

I don't think the mapping using named register variables is what you
are looking for.

Locate fault status and address via appropriate memory mapped NVIC
registers (as you apparently did already) and determine the address of
the faulting instruction by retrieving the return address from stack and
subtracting 2. The code example posted by 900ss illustrates this
approach.

--
Marcus
http://www.doulos.com/arm/
Author: 900ss D. (900ss)
Posted on:

Hi Jerry,

have the code converted to GNU (Codesourcery Toolchain).

The assembler code:
.type _hard_fault_handler_asm, %function

_hard_fault_handler_asm:
  TST LR, #4
  ITE EQ
  MRSEQ R0, MSP
  MRSNE R0, PSP
  B hard_fault_handler_c

The C code (modified a little):
// hard fault handler in C,
// with stack frame location as input parameter
void hard_fault_handler_c(unsigned int * hardfault_args)
{
unsigned int stacked_r0;
unsigned int stacked_r1;
unsigned int stacked_r2;
unsigned int stacked_r3;
unsigned int stacked_r12;
unsigned int stacked_lr;
unsigned int stacked_pc;
unsigned int stacked_psr;
unsigned int rBFAR;
unsigned int rCFSR;
unsigned int rHFSR;
unsigned int rDFSR;
unsigned int rAFSR;

stacked_r0 = ((unsigned long) hardfault_args[0]);
stacked_r1 = ((unsigned long) hardfault_args[1]);
stacked_r2 = ((unsigned long) hardfault_args[2]);
stacked_r3 = ((unsigned long) hardfault_args[3]);

stacked_r12 = ((unsigned long) hardfault_args[4]);
stacked_lr = ((unsigned long) hardfault_args[5]);
stacked_pc = ((unsigned long) hardfault_args[6]);
stacked_psr = ((unsigned long) hardfault_args[7]);

rBFAR = (*((volatile unsigned long *)(0xE000ED38)));
rCFSR = (*((volatile unsigned long *)(0xE000ED28)));
rHFSR = (*((volatile unsigned long *)(0xE000ED2C)));
rDFSR = (*((volatile unsigned long *)(0xE000ED30)));
rAFSR = (*((volatile unsigned long *)(0xE000ED3C)));

printf ("[Hard fault handler]\n");
printf ("R0 = %x\n", stacked_r0);
printf ("R1 = %x\n", stacked_r1);
printf ("R2 = %x\n", stacked_r2);
printf ("R3 = %x\n", stacked_r3);
printf ("R12 = %x\n", stacked_r12);
printf ("LR = %x\n", stacked_lr);
printf ("PC = %x\n", stacked_pc);
printf ("PSR = %x\n", stacked_psr);
printf ("BFAR = %x\n", rBFAR);
printf ("CFSR = %x\n", rCFSR);
printf ("HFSR = %x\n", rHFSR);
printf ("DFSR = %x\n", rDFSR);
printf ("AFSR = %x\n", rAFSR);

while(1);

return;
}
Author: Jerry Milner (jrmymllr)
Posted on:

Thanks again 900ss.  I'm not too good at doing asm on ARM, especially
the GCC syntax.

Reply

Entering an e-mail address is optional. If you want to receive reply notifications by e-mail, please log in.

Rules — please read before posting

  • Post long source code as attachment, not in the text
  • Posting advertisements is forbidden.

Formatting options

  • [c]C code[/c]
  • [avrasm]AVR assembler code[/avrasm]
  • [code]code in other languages, ASCII drawings[/code]
  • [math]formula (LaTeX syntax)[/math]






webmaster@embdev.netContactAdvertising on EmbDev.net