I have an assembly function which is called by the fault interrupt. This assembly code places the stack pointer (SP) into a register (r0). This assembly code then calls a C function which has one parameter, and the value value of r0 gets passed into it. The problem is, the C function messes with r0 at the beginning and sometimes trashes it. It never returns from this C function since its job is to display fault info on an LCD, then the micro must be reset. So, how can I prevent GCC from using a specific register so its integrity is maintained?
I don't know about GCC, but 'register' is the standard.
There is no such thing as a "standard" for an inherently nonstandard operation ;-). Nevertheless GCC does have a (very nonstandard) way to assign specific registers and it does indeed use the keyword "register". See http://gcc.gnu.org/onlinedocs/gcc/Explicit-Reg-Vars.html. Note though that this could turn out to be an answer to the wrong question. I do not understand what you really want to achieve. And a function should no trash its parameter if it is used later, though it could move it to some other place.
Why does that C function overwrite its own parameters before using them?
A. K. wrote: > There is no such thing as a "standard" for an inherently nonstandard > operation ;-). Nevertheless GCC does have a (very nonstandard) way to > assign specific registers and it does indeed use the keyword "register". > See http://gcc.gnu.org/onlinedocs/gcc/Explicit-Reg-Vars.html. > OK, read 'common'. > Note though that this could turn out to be an answer to the wrong > question. I do not understand what you really want to achieve. And a > function should no trash its parameter if it is used later, though it > could move it to some other place. An example is Keil C51 and Cypress PSoC. I don't remember the right one. There is a short parameter passing within registers, and a long parameter passing in the 'common' way on the stack. Parameter lists exceeding the free register map are loaded on the stack. If Assembler and C think different, the code crashes!
I assume you're talking about AVR CPUs. Then, r0 is a poor choice for parameter passing, since it is the target of some assembly language instructions (lpm and mul come to mind). They overwrite r0 and I doubt any compiler will take care of saving r0 before and restoring it after those particular instructions.
Sorry, youst see you're taking about an ARM. Forget my comment above.
APCS says that function parameters are passed in r0 to r3. And this usually works fine when calling C-Code from assembler code. So it appears there is a problem with your C function. How does it look like and what is the assebler code that is generated by the compiler?
I think I need to elaborate a bit. This is an ARM Cortex-M3. I'm trying to write a fault handler that will display the fault exception stack on my LCD. So I have this function, which is the ISR for any faults: IntDefaultHandler: TST LR, #4 ITE EQ MRSEQ R0, MSP MRSNE R0, PSP b hard_fault_handler_c I didn't come up with this strategy as it seems to be a common way of writing a fault handler. But the idea is that the C function will take the contents of r0 as the first parameter, which should be the location on the stack that contains the fault exception frame. Initially, the above code was inside a C function using __asm() but GCC put some extra instructions at the beginning. So I moved it to a assembly file. Here's essentially what is in hard_fault_handler_c: void hard_fault_handler_c(unsigned int * hardfault_args) { unsigned char y[20]; //used for string processing //display hardfault_args[0], hardfault_args[1]...hardfault_args[7] while(1); never leave } But, in looking at the generated asm I see that GCC is messing with r0: void hard_fault_handler_c(unsigned int * hardfault_args) { 10e30: b580 push {r7, lr} 10e32: b088 sub sp, #32 10e34: af00 add r7, sp, #0 10e36: 6078 str r0, [r7, #4] unsigned char y[20]; What happens sometimes is that hardfault_args is one word, 4 bytes, offset from where it should be. I'm going off the assumption that the asm code at the start of hard_fault_handler_c is modifying r0. So, that's why I'm trying to prevent hard_fault_handler_c from modifying r0. Or, if there's a better way, I'd gladly do that.
The assembly code shown above obvously is not messing around with r0, it just saves its contents to some place on the stack. Maybe because there is some significant amount of varables handled before the parameter is used, so the compiler frees r0 for better use, or maybe because optimzation is disabled and the regular unoptimized place of variables is in memory.
A failsafe version of a hard fault handler should initialize its stack pointer with a known safe value before first use within the handler, because one likely reason of a hard fault is an invalid stack pointer value.
Are we talking about unimplemented interrupts in general or about the CM3's hard fault vector? For unimplemented interrupts, you may safely assume a valid stack frame. Not so for true hard faults however, because the hard fault handler may get entered because some stack push operation by a regular fault handler failed. So it is not safe to assume a regular exeption frame on the stack on entry of the hard fault handler. Look at BFAR.
Correction: The bus fault status register should be consulted prior to any assumption on stack contents. If bus and memory faults are enabled, the same caution could be necessary for them as well. IMHO the CM3 core manual isn't very clear in this respect.
If it 'sometimes' occur, than think of the possibility of an stack overflow.
Abdul K. wrote: > If it 'sometimes' occur, than think of the possibility of an stack > overflow. I should be more clear on "sometimes". My stack is definitely not overflowing. I've checked and it's far from hitting the limit. What I've done is placed artificial bugs in different areas that I can trigger selectively. I write a value to an invalid memory location to test the fault handler. There are three of these artificial bugs. Two of them generate a correct pointer to the stack frame. The third one generates a pointer to a stack from that is offset by 1 word. It's perfectly repeatable. My code in general is stable; I just want a good fault handler in case it does fault.
A. K. wrote: > Are we talking about unimplemented interrupts in general or about the > CM3's hard fault vector? For unimplemented interrupts, you may safely > assume a valid stack frame. Not so for true hard faults however, because > the hard fault handler may get entered because some stack push operation > by a regular fault handler failed. So it is not safe to assume a > regular exeption frame on the stack on entry of the hard fault handler. > > Look at BFAR. It would be great if I could just display BFAR, but it never seems to contain a valid value. The only time I've gotten it to display a valid value was when I (intentionally) wrote to a peripheral without enabling it first. Otherwise, BFAR contains it's own address after the artificial bugs I've introduced (see my previous post about that)
A. K. wrote: > The assembly code shown above obvously is not messing around with r0, it > just saves its contents to some place on the stack. Maybe because there > is some significant amount of varables handled before the parameter is > used, so the compiler frees r0 for better use, or maybe because > optimzation is disabled and the regular unoptimized place of variables > is in memory. Ok, yes you're correct. I'm not an ARM asm expert and was interpreting the str instruction incorrectly.
Jerry Milner wrote: > A. K. wrote: >> Are we talking about unimplemented interrupts in general or about the >> CM3's hard fault vector? For unimplemented interrupts, you may safely >> assume a valid stack frame. Not so for true hard faults however, because >> the hard fault handler may get entered because some stack push operation >> by a regular fault handler failed. So it is not safe to assume a >> regular exeption frame on the stack on entry of the hard fault handler. >> >> Look at BFAR. > > It would be great if I could just display BFAR, but it never seems to > contain a valid value. The only time I've gotten it to display a valid > value was when I (intentionally) wrote to a peripheral without enabling > it first. Otherwise, BFAR contains it's own address after the > artificial bugs I've introduced (see my previous post about that) Sorry about all the posts. I'm dealing with the hard, bus, and usage fault vectors.
Jerry Milner wrote: > It would be great if I could just display BFAR, but it never seems to > contain a valid value. Maybe you didn't actually have a bus fault. > What I've done is placed artificial bugs in different areas that I > can trigger selectively. I write a value to an invalid memory > location to test the fault handler. There are three of these > artificial bugs. Two of them generate a correct pointer to the > stack frame. The third one generates a pointer to a stack from that > is offset by 1 word. It's perfectly repeatable. This is great, so can tell us exactly what the respective stack pointer values were immediately before the fault was triggered and what the r0 looks like immediately before entering hard_fault_handler_c(). It might also help to see a little more of hard_fault_handler_c()'s code. Note that Cortex-M3 supports optional automatic exception stack alignment. If the stack was less than eight byte aligned when the exception occurred, exception entry will subtract four (a word) from the stack pointer. -- Marcus http://www.doulos.com/arm/
Marcus Harnisch wrote: > Jerry Milner wrote: >> It would be great if I could just display BFAR, but it never seems to >> contain a valid value. > > Maybe you didn't actually have a bus fault. > I think it is, because CFSR indicates an imprecise bus fault, and if I put a unique function in the bus fault location on the interrupt vector table, it jumps to it. However, the plan is to have hard, bus, and usage faults vector to the same function. >> What I've done is placed artificial bugs in different areas that I >> can trigger selectively. I write a value to an invalid memory >> location to test the fault handler. There are three of these >> artificial bugs. Two of them generate a correct pointer to the >> stack frame. The third one generates a pointer to a stack from that >> is offset by 1 word. It's perfectly repeatable. > > This is great, so can tell us exactly what the respective stack > pointer values were immediately before the fault was triggered and > what the r0 looks like immediately before entering > hard_fault_handler_c(). It might also help to see a little more of > hard_fault_handler_c()'s code. > I'll collect this information later today and post here. As for what is in hard_fault_handler_c(), I'm just using a sprintf type function to place the string in "y", then a function I wrote that displays "y".
1 | usprintf (y, "R0: 0x%08x", hardfault_args[0]); |
2 | lcd_DspStr(y, LCD_LINE_NUM(20), INVERT_OFF, Terminal6x8); |
3 | //and repeating for hardfault_args[1], hardfault_args[2], etc
|
> Note that Cortex-M3 supports optional automatic exception stack > alignment. If the stack was less than eight byte aligned when the > exception occurred, exception entry will subtract four (a word) from > the stack pointer. I found this information, but am a little confused by it. At first I thought if the value of (in this case) r0 wasn't evenly divisible by 8, I'd adjust it by one word. However that didn't reliably fix it. So then I tried setting STKALIGN during initialization. That didn't fix it either, and actually might have been worse. I don't know if what I tried is correct, but it was how I interpreted it. > > -- > Marcus > http://www.doulos.com/arm/
Jerry Milner wrote: > Marcus Harnisch wrote: >> Maybe you didn't actually have a bus fault. > > I think it is, because CFSR indicates an imprecise bus fault, and if I > put a unique function in the bus fault location on the interrupt vector > table, it jumps to it. However, the plan is to have hard, bus, and > usage faults vector to the same function. With imprecise faults all bets are off anyway. An imprecise fault may be pended and can occur way after the offending instruction has executed. Registers might have changed and therefore the stack might not contain information that is helpful in nailing down the issue. So you are saying that two of your injected faults are behaving nicely. Are these two also triggering imprecise faults? If these two are precise faults and only the "odd" one is imprecise you are done. What you also need to tell us is how you make the exceptions reuse the same handler. Are you putting the same function name into the vector table with all these exceptions enabled or are you taking advantage of fault escalation? >> This is great, so can tell us exactly what the respective stack >> pointer values were immediately before the fault was triggered and >> what the r0 looks like immediately before entering >> hard_fault_handler_c(). > > I'll collect this information later today and post here. Thanks. >> Note that Cortex-M3 supports optional automatic exception stack >> alignment. > > I found this information, but am a little confused by it. At first I > thought if the value of (in this case) r0 wasn't evenly divisible by 8, > I'd adjust it by one word. However that didn't reliably fix it. Of course not. If your SW (printf perhaps) depends on an aligned stack, aligning r0 doesn't help. The stack pointer register (MSP or PSP needs to be aligned). Since a copy of your stack pointer will be stored in r0 it'll consequently be aligned, too. > So then I tried setting STKALIGN during initialization. That didn't > fix it either, and actually might have been worse. In which way? -- Marcus
Marcus Harnisch wrote: > Jerry Milner wrote: > > With imprecise faults all bets are off anyway. An imprecise fault may > be pended and can occur way after the offending instruction has > executed. Registers might have changed and therefore the stack might > not contain information that is helpful in nailing down the issue. > > So you are saying that two of your injected faults are behaving > nicely. Are these two also triggering imprecise faults? If these two > are precise faults and only the "odd" one is imprecise you are done. > All three are imprecise. CFSR is 0x400 and BFAR always contains it's own address with any of the three faults. > What you also need to tell us is how you make the exceptions reuse the > same handler. Are you putting the same function name into the vector > table with all these exceptions enabled or are you taking advantage of > fault escalation? > I'm putting the same function name into the vector table. Although, I could just as well not enable the other exceptions and let it escalate to the hard fault, if that is preferable. Actually I was doing that for awhile, but now it doesn't make sense to do that if I'm using the same handler. >>> Note that Cortex-M3 supports optional automatic exception stack >>> alignment. >> >> I found this information, but am a little confused by it. At first I >> thought if the value of (in this case) r0 wasn't evenly divisible by 8, >> I'd adjust it by one word. However that didn't reliably fix it. > > Of course not. If your SW (printf perhaps) depends on an aligned > stack, aligning r0 doesn't help. The stack pointer register (MSP or > PSP needs to be aligned). Since a copy of your stack pointer will be > stored in r0 it'll consequently be aligned, too. I'm not sure if usprintf depends on an aligned stack. But, in using the JTAG debugger in hard_fault_handler_c() I can see that r0 in these cases points to the incorrect stack location, at least based upon what the stacked PC should approximately be. When r0 doesn't point to the correct location, it's always one word away from what seems like a plausible stacked PC value. > >> So then I tried setting STKALIGN during initialization. That didn't >> fix it either, and actually might have been worse. > > In which way? I'll have to go back and check this one when I can get to the setup later, but it seems like r0 was incorrect even for the injected faults that previously produced good results. But, I need to verify this one. I had high hopes STKALIGN would fix my problem, but it didn't so I abandoned it shortly afterward. > > -- > Marcus
Jerry Milner wrote: > All three are imprecise. CFSR is 0x400 and BFAR always contains it's > own address with any of the three faults. Imprecise aborts don't update BFAR. > I'm putting the same function name into the vector table. Although, I > could just as well not enable the other exceptions and let it escalate > to the hard fault, if that is preferable. Actually I was doing that for > awhile, but now it doesn't make sense to do that if I'm using the same > handler. An escalated imprecise bus fault won't be pended but force the handler to be called. > I'm not sure if usprintf depends on an aligned stack. But, in using the > JTAG debugger in hard_fault_handler_c() I can see that r0 in these cases > points to the incorrect stack location, at least based upon what the > stacked PC should approximately be. When r0 doesn't point to the > correct location, it's always one word away from what seems like a > plausible stacked PC value. But what did r0 look like before entering hard_fault_handler_c()? In other words, did r0 ever contain the expected value? -- Marcus
Marcus Harnisch wrote: > >> I'm not sure if usprintf depends on an aligned stack. But, in using the >> JTAG debugger in hard_fault_handler_c() I can see that r0 in these cases >> points to the incorrect stack location, at least based upon what the >> stacked PC should approximately be. When r0 doesn't point to the >> correct location, it's always one word away from what seems like a >> plausible stacked PC value. > > But what did r0 look like before entering hard_fault_handler_c()? In > other words, did r0 ever contain the expected value? Good question. I'll check this too. For some reason the registers don't always appear in my Eclipse/OpenOCD setup. I don't know which is at fault, but I can get them to appear sometimes by restarting the session or restarting Eclipse. > > -- > Marcus
Marcus Harnisch wrote: > >> I'm not sure if usprintf depends on an aligned stack. But, in using the >> JTAG debugger in hard_fault_handler_c() I can see that r0 in these cases >> points to the incorrect stack location, at least based upon what the >> stacked PC should approximately be. When r0 doesn't point to the >> correct location, it's always one word away from what seems like a >> plausible stacked PC value. > > But what did r0 look like before entering hard_fault_handler_c()? In > other words, did r0 ever contain the expected value? Here's what I found: the value that r0 acquired in IntDefaultHandler (the asm ISR) was passed to hardfault_args in hard_fault_handler_c() every time intact, and stayed intact. I actually added a fourth "bug" in a completely different function to make things interesting. It's the exact same errant code as the other three bugs. Below is a summary of how each fault behaves. I'll number each function that contains a bug. Remember that one function contains two faults that can be separately activated; one fault in this function is inside an if(), and the second one is inside a corresponding else if(). The if() and else if() each have their own local variables that I assume would have to be pushed onto the stack. Function "Bug" Address Correct? Stacked PC r0 #1 0x100D8 correct 0x100E6 0x20002ED8 #1 0xFF7E incorrect (in LR) 0x18E0 0x20002ED8 #2 0x5C28 incorrect (in LR) 0xD90 0x20003030 #3 0x3D48 correct 0x3D5A 0x20003028 The second column is the address of the instruction that writes to an illegal address and causes the fault. The third column indicates if the stacked PC value seems correct; if not, I indicated what position it appears in. So, for this data, when the stacked PC value didn't seem correct, I instead found the value that I believe should have been the PC in the LR position. In other words, I believe that r0/hardfault_args is one word too small in these cases. The fourth column is the value in the stacked PC position. In the "correct" ones, the stacked PC value is very close to the "bug" address. In the "incorrect" ones, I found a similarly close value offset by one, in the stacked LR position (if r0/hardfault_args is pointing to the stacked r0 value, then stepping thru the rest of the stacked values: r1, r2, r3, r12, LR, PC). The last column is the pointer to the supposed stacked r0 value, in other words, r0/hardfault_args. As for STKALIGN, I tried writing 0x20 to it at initialization, and it made no difference.
Jerry Milner wrote: > So, for this data, when the stacked PC value didn't seem correct, I > instead found the value that I believe should have been the PC in > the LR position. In other words, I believe that r0/hardfault_args > is one word too small in these cases. Would that assumption correspond with the other stacked register values? Try disabling Bus Fault and Mem Manage Fault and escalate them to Hard Fault. See if that makes a difference. Again: The observed faults are imprecise and that means the return address may not be related to the instruction that caused the abort. The fact that you see a "close" value in stacked lr might just be a coincidence. -- Marcus
Marcus Harnisch wrote: > Jerry Milner wrote: >> So, for this data, when the stacked PC value didn't seem correct, I >> instead found the value that I believe should have been the PC in >> the LR position. In other words, I believe that r0/hardfault_args >> is one word too small in these cases. > > Would that assumption correspond with the other stacked register values? I frequently see the same/similar values in the immediate vicinity, but can't say for sure how consistent that is unless I start keeping track. I do however frequently see 0xfffffff1 or 0xfffffff9 in the word after the pointer, and usually see the invalid address I'm writing to in the vicinity (0x30000000). > > Try disabling Bus Fault and Mem Manage Fault and escalate them to Hard > Fault. See if that makes a difference. I'll try that later today when I'm able to get to the setup and I'll report back. > > Again: The observed faults are imprecise and that means the return > address may not be related to the instruction that caused the > abort. The fact that you see a "close" value in stacked lr might just > be a coincidence. > Understood, and I can't safely assume anything. However, in the case of the two faults that are providing incorrect stack pointer values, I believe the probability of both containing a plausible stacked PC value in LR strictly by chance is fairly small. Is it possible that LR would contain the return address? The CM3 manual says: The LR receives the return address from PC when a Branch and Link (BL) or Branch and Link with Exchange (BLX) instruction is executed. The LR is also used for exception return. Maybe there's a good way to identify more markers I could use, for example, if I could display the values of r0-r3 and r12 immediately before the faulty line of code, then try to match it up with the stacked values? I'm not sure how well that would work. It might be worth a try
Jerry Milner wrote: > Understood, and I can't safely assume anything. However, in the case of > the two faults that are providing incorrect stack pointer values, I > believe the probability of both containing a plausible stacked PC value > in LR strictly by chance is fairly small. Is it possible that LR would > contain the return address? I certain special cases perhaps. One instruction causes an imprecise (asynchronous) fault. A subsequent instruction is a BX lr which returns from a subroutine. The exception executes right there. If the timing is just right, I suppose the exception return address (stacked pc) could be the same as stacked lr. > The LR is also used for exception return. Yes, but not as return address. It is used to inform the exception exit how to return. The return address comes from the stacked pc -- always. > Maybe there's a good way to identify more markers I could use, for > example, if I could display the values of r0-r3 and r12 immediately > before the faulty line of code, then try to match it up with the stacked > values? I'm not sure how well that would work. It might be worth a try That's what I meant with this: >> Would that assumption correspond with the other stacked register values? -- Marcus http://www.doulos.com/arm/
Just something to add I discovered. The stacked PC values that seem to be incorrect are in fact the addresses of functions which are called very soon or immediately after the faulty line of code. So this would seem to explain things, but LR's purpose must be to contain the return address back to the original function.
Jerry Milner wrote: > The stacked PC values that seem to be incorrect are in fact the > addresses of functions which are called very soon or immediately > after the faulty line of code. There you go. > So this would seem to explain things, but LR's purpose must be to > contain the return address back to the original function. Not in case of exceptions. It only appears to be the case to keep the programmers model as simple and straightforward as possible. That way you can return from exceptions and subroutines the same way. Note that this is unique to ARM Cortex-M processors. Other ARM cores use a different model. -- Marcus
Marcus Harnisch wrote: > There you go. > >> So this would seem to explain things, but LR's purpose must be to >> contain the return address back to the original function. > > Not in case of exceptions. It only appears to be the case to keep the > programmers model as simple and straightforward as possible. That way > you can return from exceptions and subroutines the same way. Note that > this is unique to ARM Cortex-M processors. Other ARM cores use a > different model. > > -- > Marcus I disabled the other exceptions, forcing them to escalate to hard faults, and get the same results. So I think it's safe to say this is working as it should, but maybe not as I expect!
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.