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?
Hi Jerry,
this is helpful for you I guess:
https://my.st.com/public/STe2ecommunities/mcu/Lists/ARM%20CortexM3%20STM32/Hard%20Fault%20and%20stack%20pushing
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.
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/
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.
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.
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/
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;
}
|
Thanks again 900ss. I'm not too good at doing asm on ARM, especially
the GCC syntax.
Sorry for warming it up again,
but I tried those code above but I cannot reliably execute this code.
Even the Hardfault is set into the vector table correctly it is not
called.
I reduced the code to just call my default interrupt function but it
works only with other interrupts, not the ARM special ones.
But I have some speciality:
My system has only the reset vector and the stack pointer in the flash.
At boot time the complete vector table is created in RAM and the
SCB->VTOR pointer is set to this table. This is because the operating
system allowes dynamic registration of interrupts.
Is it possible, that the Hardfault is always called from the flash
vector table?
Do I have to setup any other StackPointer area besides the standard one?
Best regards
Ulrich
Ulrich P. wrote:
> My system has only the reset vector and the stack pointer in the flash.
> At boot time the complete vector table is created in RAM and the
> SCB->VTOR pointer is set to this table.
The minimal vector table must contain initial SP, as well as Reset-,
NMI- and HardFault Handlers. Having just two entries isn't safe.
> Is it possible, that the Hardfault is always called from the flash
> vector table?
Only if the fault occurs before the VTOR change. Perhaps also, if the
vector table alignment is wrong -- don't know, never tried this.
Regards
Marcus
Hi Marcus,
Everything works fine with the RAM vector table. The Exception Vectors
are fetched from there. Adn there is no exception before the VTOR is
changed.
The problem is, that my system is running in Thread mode and per default
privileged is set. I configure
SCB->SHCSR |= (SCB_SHCSR_MEMFAULTENA|SCB_SHCSR_BUSFAULTENA|SCB_SHCSR_USGFAULTENA);
|
but if I check via debug output the fault handlers are disabled, the
register shows 0x00020000 instead of 0x00070000.
Now I added another setup of SHCSR register somwhere later in the code
and now the hardfaults are intercepted and interpreted. But I chased any
access to SHCSR and IntEnable/IntDisable and so on, there isn't any line
of code modifying this set of registers.
I cam to this as the self made handler works fine if there is a running
debug session via JTAG active. If no JTAG no hardfault handler. Only by
writing SHCSR two times
Very funny.
You mentioned about _set_CONTROL(0x00) doesn't set Privileged if you are
not privileged.
Question 1: How to become privileged?
Question 2: The system runs already in privileged mode.
CONTROL is 0x00000000
MSP is 0x200023b4
PSP is 0x20001a00
SHCSR is 0x00020000
|
If I then give a debug command to my device it again sets SHCSR and it
works fine:
CONTROL is 0x00000000
MSP is 0x200023b4
PSP is 0x20001a00
SHCSR is 0x00070000
|
If I then force an unaligned access my own hardfault handler takes over
control.
Ulrich P. wrote:
> You mentioned about _set_CONTROL(0x00) doesn't set Privileged if you are
> not privileged.
> Question 1: How to become privileged?
By executing an exception. Often an SVC is used for this purpose.
> Question 2: The system runs already in privileged mode.
Sure. Your code example showed that something about privileges in C-M3
wasn't fully understood (no offense), otherwise you wouldn't have tried
to gain privileges. What exactly the issue is, I don't know from the
bits you posted.
--
Marcus
Hi Marcus,
it is a bit complicated to post the code as it is a complete operating
system. Even it is open source and BSD licensed it would break the
upload limits :)
And it is a bit difficult to pick out the sources that rely to that
fault handler.
But you're right, I am new to Cortex, or at least to the part of the
handlers. The other 98% of drivers for any hardware work fine that I
ported already.
So, the cortex is working in thread mode from start onwards and it
states that it is set privileged. The documentation tells that I need to
be in exactly that state to setup these bits for the handlers. And now
you tell me that I have to be in such a handler to switch the handler...
Ok, that was not mentioned in the documentation I read, but how so ever
I'll try that next time I have a short break.
I already had the idea to isolate OS and Application a bit by using the
different modes. May be this is a good training :)
Thanks for the hint!
Best regards
Ulrich
Ulrich P. wrote:
> So, the cortex is working in thread mode from start onwards and it
> states that it is set privileged.
OK.
> The documentation tells that I need to be in exactly that state to
> setup these bits for the handlers.
Yes.
> And now you tell me that I have to be in such a handler to switch the
> handler...
I never said that. All I said was that you had to be in privileged mode.
The fact that your code tried to gain privileges by clearing CONTROL[0]
suggested that you weren't quite sure about that. Never mind that this
attempt to elevate your privileges wouldn't have worked anyway. One way
to transition from unprivileged to privileged operation in a program is
an SVC exception.
Since your CONTROL register value seems to be 0, I would also have a
look at the stack pointer value. In your code snippet you are setting
PSP, while the CONTROL[1] bit, when 0, configures Thread mode to use
MSP.
--
Marcus
Hi!
Sorry, I misunderstood you.
Ok, the debug output showed that it is running privileged. I just double
checked, that it is running by setting the mode that was already set.
The MSP/PSP I check again later.
The funny thing is, what I wrote some posts above:
The system runs in privileged mode from reset onwards. I set the flags
for the handlers somwhere in the init part of the idle task. My main is
called and all my threads are running fine. I use my commander to check
the values of the register and see I am still running in P-mode. But I
see that the flags are not set 7 but stay as 2.
With the same commander, without calling _set_CONTROL() I just write 7
to the same register and by calling the same commander I read it out and
it is 7.
I then added the setting of 7 to the main() and it doesn't work too.
Only if I set it in the commander it is set correctly and it keeps its
state until the next reset.
Ah, and it works without any problem, even I set 7 in the init() or
idle_init() if the debugger is connected and runs the code.
So I have to find out why I can set the handler bits to 7 if the
debugger controls the chip or why I can set it 'manually' in the
commander during runtime but not in any other function that is called at
runtime. And that in a software the does not change the modes of the CPU
at all.
Best regards
Ulrich
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]
|