I'm using an AT91SAM7 uC and have inherited some C code which is giving me fits. When I run this code I get a data abort error. I've tracked down the offending line of code, and it appears to be caused by something like this:
n = (UINT8) ((UINT16*)(pObject->pVar))[0];
pObject->pVar points to an instance of a structure like this:
typedef __packed struct{ UINT16 v1; UINT32 v2; UINT32 v3; UINT32 v4; UINT32 v5; }OBJTYPE; OBJTYPE xxx = {0,0,0,0,0};
When I stop the debugger on the line that causes the data abort error, I can see that pObject->pVar is pointing to 0x00200173. Is the data abort error happening because that object is not on a 32-bit boundary in memory? If so, how can I [easily] fix that? I've been going through all the online doc's reading about __packed, #pragma pack(n), adding unions to typedefs, etc.; but all the things I've been trying have not fixed the problem.
Is there an easy way (besides the __at__ attribute) to get variables like the above structure to be aligned on 32-bit boundaries?
Can anyone give me a suggestion for how to resolve this problem?
Help... Dave.
"pObject->pVar points to an instance of a structure like this..."
It probably doesn't. What is the code that sets the value of pObject->pVar?
"Is the data abort error happening because that object is not on a 32-bit boundary in memory?"
I would wager that it is on a 32-bit boundary. Packing directives deal with packing structure members, not the alignment of the entire structure.
"Can anyone give me a suggestion for how to resolve this problem?"
I'd go back and take a look at how pObject->pVar gets set.
Thanks Dan.
According to the map file, the variable that this pointer is supposed to be pointing to is located at 0x00200173. When I stop on the line of code that's trying to access this variable via the pointer, the pointer is pointing to 0x00200173. That makes me think it really is pointing to the right structure. I don't see how that address is 32-bit aligned though.
If I use the debugger to look at the RAM the pointer points to, the right data is there.
If I make the pointer point to an address that is 32-bit aligned (although it doesn't point to the start of the structure anymore), the data abort error goes away.
If I remove __packed from the struct's typedef and rebuild the code, the map file shows that the var then does get aligned on a 32-bit boundary; and the data abort error goes away too.
I'm still confused. I don't see how this code can work if these structures don't get aligned on 32-bit boundaries when I use __packed in their typedef's.
Dave.
have you tried to patch the structure?
Hi David,
I believe you shoud remove the casting of the pointer: (UINT16*)(pObject->pVar). It appears that the cast removes the __packed qualifier from the pointer, so the compiler uses an aligned memory access instead of unaligned one. If the code in question tries to access a structure member, why wouldn't it just do it: n = (UINT8)(pObject->pVar->v1) ?
That's roughly equivalent to asking if there's an easy way to make triangular wheels roll smoothly, short of making the road a sequence of perfectly shaped arcs fitting triangular wheels of just that size.
The answer to both questions is: "Don't do that, then!". Packing structures and pointer gymnastics like those in the OP are two concepts that just don't mix, period. For the pointer voodoo to work, the standard semantics of C have to be in action. Stuff like __packed breaks that premise.
Thanks for all the suggestions.
The bottom line is that there's too much of this inherited code, and it's full of stuff like this. The problem is that the pObjEntry pointer points to many different kinds of structures, that are all part of an object dictionary for an EtherCAT slave controller. I probably could remove the typecasting to get the first structure element, because it defines the number of entries in this object's dictionary entry. However, I'd need to use typecasts for the remaining elements because they're different for different objects. I'll try your suggestions out and see if that gets me past this roadblock.
The object dictionary is "fixed" once the code has been written and built. It's not dynamic. So, another possible fix is to use a #define to build the code without, and then with the __packed attribute. I could then parse the map file to get the absolute addresses of all these objects, and then use the "at" attribute to hard code the addresses of the object dictionary elements to addresses that are aligned on 32-bit boundaries, and then change the #define to use the __packed attribute.
I know it sounds ugly, but it sounds like the easiest way to be able to use __packed and be able to use typecasts to get stuff out of all these objects at runtime.
The bottom line is that there's too much of this inherited code, and it's full of stuff like this.
So you've got yourselves wedged between a rock and a hard place. That means soft measures will no longer work. You have to bite the bullet and get into gear to fix things in earnest. Yes, that'll hurt. But you'll be better off having done it in the medium term. It's already quite amazing that this cruft ever appeared to work correctly --- but that's no excuse to leave it in that state now you've diagnosed it.
but it sounds like the easiest way to be able to use __packed and be able to use typecasts to get stuff out of all these objects at runtime.
No. The easiest way would be to remove any and all appearances of __packed from that source code. They're causing you nothing but problems the rest of the code is ill-prepared to handle, and with the method you've described you would be losing any advantages it promises anyway. So just lose __packed completely, and never look back.
"I'm still confused. I don't see how this code can work if these structures don't get aligned on 32-bit boundaries when I use __packed in their typedef's."
You never did show pObject's declaration. Is it an 'OBJTYPE*' (i.e., points to a __packed structure)? I sounds like the compiler does not agree that it's pointing to unaligned data.
However, I'd need to use typecasts for the remaining elements because they're different for different objects.
Isn't it something unions can be used for?
That, or if one somehow feels locked into using pointer casts, at least make them struct pointer casts, i.e. instead of
*(first_element *)(&one_struct)
make it
((otherstruct *)(& one_struct))->first_element
Among other things, this has the advantage of seamlessly working for other than the first element, too, and of continuing to work if the type of some elements has to be changed.
Okay... I know you're right... there's no easy way out. Hope is always the last thing to go. Thanks for all the advice and encouragement. Wish me luck!
Mmmmmm......
(Sorry for my limited English ability.)
To help myself to clarify some concepts, I hope that I can confirm one thing with the help from you all.
If the processor is executing
n = (UINT8) ((UINT16*)(0x00200173))[0];
And 0x00200173 is somewhere in RAM, Why Data Abort Exception is triggered?
A 16-bit access (UINT16*) should be made to an even address. Had the compiler known that the data was packed, it would have performed two 8-bit accesses.
To just perform one 8-bit access, you should have had (UINT8*).
nice to c some revent information on following web pages. microcontroller51.blogspot.com/ and see also http://picinf.blogspot.com/
Dr Gull? Don't you mean Dr Spam?