cancel
Showing results for 
Search instead for 
Did you mean: 

Hard fault caused by unaligned access

RBrec.1
Associate III

Hi, 

I have an issue when trying to copy a struct by direct assignment on STM32H743; 

The code: 

 

 

void function(const structName_t *incomingStruct) {

Signal_t sendSignal;

sendSignal.desiredStruct = *incomingStruct;  // <-- Causes unaligned access

..

}

 

 

If desiredStruct consists of only 8 or 16 bits fields, everything works perfectly. If however, one or more of the fields is 32-bits, then the SCB->CFSR->UNALIGNED bit goes high and the programs enters the hard_Fault handler after line 5. I do have a quick fix on this; declaring the desiredStruct as __attribute__((__packed__)). However, as other projects running on STM32F7 use the same library without any issues, I would like to know if anybody has a local solution, or any idea of what's going on. As I understand it, it is the compilers job to add the desired padding to the data to have it aligned, but it doesn't look like it's able to do so? I would assume the compiler ideally don't want any packed attribute, as we remove it's freedom to alter the space between each structure field. I would love to have more input on this specific problem :)

Thanks

 

EDIT: Copying each struct field one by one also works, it is only the operation above that causes trouble. 

23 REPLIES 23

@SofLit Looking at the specific instructions when running the line above:

assembly.JPG

It is the bottom line that fails. Further details the UNALIGN_TRP bit is zero. I guess receiving confirmation that an illegal unaligned access is performed was kinda dissatisfying, because my main question becomes why the compiler would allow such an instruction. But I guess this problem at least is of the hook for ST :grinning_face_with_sweat: 

Another wild guess: it may be related to enum shortening when packed. In C, enum is normally if int type but it may be force to a shorter type by compiler option or by packed attribute. The declaration of SubConfig_t above suggests that enum is expected to be 8-bit, so -fshort-enums is/should be used.

@waclawek.janyou were right. packed will have interesting results only with structures inside structures. It might be the case here, connected with the (over)use of typedefs in the code shown. I wonder if how packed relates to typedef.

There are few good reasons for not using typedefs... ;)

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

How exactly do you call doSomething()? I am interested in the way how do you determine the second, sendData parameter. Don't you typecast a pointer taken from a pool of bytes (communication buffer),?

JW

Pavel A.
Evangelist III

So the failing instruction is ldmia. Why does it require alignment? Is the *incomingStruct located in normal memory?

 

Maybe easier to write in code (I see sendData is a pretty *** name..)

static Config_t ConfigData;

createSignalHandler(doSomething, &ConfigData, QUEUE_CONFIG, ...);

So sending a signal to QUEUE_CONFIG which will trigger doSomething() with the signal itself and the static global struct ConfigData. So in the previous example, doSomething() is triggered by Signal_t configSignal sent to QUEUE_CONFIG, and is executed with ConfigData and configSignal as parameter 1 and 2. Hope this makes sense.

Why does it require alignment: I guess because the hw doesn't allow unaligned ldmia instructions

Is the *incomingStruct located in normal memory: Yes.

As been mentioned above, there was one previous struct which was also declared as packed. So the original question could potentially be a result by the previous packed that may have caused unalignment somehow. However, having zero packed structs still causes the original problem (fixed ages ago with the packed attribute), so now I kinda try to understand if something can fix the original problem, and hopefully this also fixes the question from the beginning.. 

Do you ever cast a pointer to this struct form a pointer to byte/halfword? It usually occurs when the data is received via comm interface and stored in a byte buffer or read from non-volatile memory as an array of bytes.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

I am interested in the way how do you determine the second, sendData parameter. Or, as you say above, configSignal (I don't see where does it come from).

Don't you typecast a pointer taken from a pool of bytes (communication buffer),?

If yes, then that's the problem. The compiler itself is innocent. The C standard says this (C99, 6.3.2.3#7):

A pointer to an object or incomplete type may be converted to a pointer to a different
object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined.

However, in practice, converting between structs and arrays of bytes is a common practice, as this is what often happens when data are transmitted or stored. There are several "schools" telling how to do this "correctly", and these influence and are influenced by particular compilers. The prevalent view in the near past was, that you should use unions; today the view shifted in favour of copying structs from/to byte arrays using memcpy. It appears, that in the gcc world, using packed structs (which are needed for precise control of layout for communication anyway) results in compiler being much more careful in accessing them, than without; however, it's a gcc extension.

JW

 

> ... ldmia. Why does it require alignment?

See SoftLit's post above - LDMIA is not in the list of LD/ST instructions which don't require [EDIT] do support [/EDIT] alignment.

JW

Pavel A.
Evangelist III

.