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:
1 | .type _hard_fault_handler_asm, %function |
2 | |
3 | _hard_fault_handler_asm: |
4 | TST LR, #4 |
5 | ITE EQ |
6 | MRSEQ R0, MSP |
7 | MRSNE R0, PSP |
8 | B hard_fault_handler_c |
The C code (modified a little):
1 | // hard fault handler in C,
|
2 | // with stack frame location as input parameter
|
3 | void hard_fault_handler_c(unsigned int * hardfault_args) |
4 | {
|
5 | unsigned int stacked_r0; |
6 | unsigned int stacked_r1; |
7 | unsigned int stacked_r2; |
8 | unsigned int stacked_r3; |
9 | unsigned int stacked_r12; |
10 | unsigned int stacked_lr; |
11 | unsigned int stacked_pc; |
12 | unsigned int stacked_psr; |
13 | unsigned int rBFAR; |
14 | unsigned int rCFSR; |
15 | unsigned int rHFSR; |
16 | unsigned int rDFSR; |
17 | unsigned int rAFSR; |
18 | |
19 | stacked_r0 = ((unsigned long) hardfault_args[0]); |
20 | stacked_r1 = ((unsigned long) hardfault_args[1]); |
21 | stacked_r2 = ((unsigned long) hardfault_args[2]); |
22 | stacked_r3 = ((unsigned long) hardfault_args[3]); |
23 | |
24 | stacked_r12 = ((unsigned long) hardfault_args[4]); |
25 | stacked_lr = ((unsigned long) hardfault_args[5]); |
26 | stacked_pc = ((unsigned long) hardfault_args[6]); |
27 | stacked_psr = ((unsigned long) hardfault_args[7]); |
28 | |
29 | rBFAR = (*((volatile unsigned long *)(0xE000ED38))); |
30 | rCFSR = (*((volatile unsigned long *)(0xE000ED28))); |
31 | rHFSR = (*((volatile unsigned long *)(0xE000ED2C))); |
32 | rDFSR = (*((volatile unsigned long *)(0xE000ED30))); |
33 | rAFSR = (*((volatile unsigned long *)(0xE000ED3C))); |
34 | |
35 | printf ("[Hard fault handler]\n"); |
36 | printf ("R0 = %x\n", stacked_r0); |
37 | printf ("R1 = %x\n", stacked_r1); |
38 | printf ("R2 = %x\n", stacked_r2); |
39 | printf ("R3 = %x\n", stacked_r3); |
40 | printf ("R12 = %x\n", stacked_r12); |
41 | printf ("LR = %x\n", stacked_lr); |
42 | printf ("PC = %x\n", stacked_pc); |
43 | printf ("PSR = %x\n", stacked_psr); |
44 | printf ("BFAR = %x\n", rBFAR); |
45 | printf ("CFSR = %x\n", rCFSR); |
46 | printf ("HFSR = %x\n", rHFSR); |
47 | printf ("DFSR = %x\n", rDFSR); |
48 | printf ("AFSR = %x\n", rAFSR); |
49 | |
50 | while(1); |
51 | |
52 | return; |
53 | }
|
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
1 | 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.
1 | CONTROL is 0x00000000 |
2 | MSP is 0x200023b4 |
3 | PSP is 0x20001a00 |
4 | SHCSR is 0x00020000 |
If I then give a debug command to my device it again sets SHCSR and it works fine:
1 | CONTROL is 0x00000000 |
2 | MSP is 0x200023b4 |
3 | PSP is 0x20001a00 |
4 | 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
Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
Log in with Google account
No account? Register here.