Struct packing problem

Hi,
Now I’ve defined the following packed struct.

MBED_PACKED(struct) Feedback
{
public:
unsigned char motorID;
float position;
float velocity;
float current;
};

When I pass the member variable of Feedback object as a reference to a function and access the reference in the function, hard fault exception happens like below.

Feedback feedback;
unsigned char a[8];
ParameterRange r;
toParameter(a, r, feedback.motorID, feedback.position);

void toParameter(unsigned char (&data)[8], ParameterRange &range, unsigned char &canID, float &position)
{
int p_int = (data[1] << 8) | data[2];
int v_int = (data[3] << 4) | (data[4] >> 4);
int t_int = (data[4] & 0xf) << 8 | data[5];

canID = data[0];
float span = range.positionRange.max - range.positionRange.min;
float offset = range.positionRange.min;
position = (int) ((p_int)*((float)((1<<16)-1))/span); <-- at this point,  hard fault exception happens

}

If I write the Feedback class without packing option or I pass a float type local variable in the caller stack as a reference or pass the feedback object as a reference to access the member variable such as feedback.position, the exception doesn’t happen.

My system is Mbed 6.8.0 and NUCLEO-F767ZI.

Here is the crash log:

  • MbedOS Fault Handler –

++ MbedOS Error Info ++
Error Status: 0x80FF013D Code: 317 Module: 255
Error Message: Fault exception
Location: 0x80043DC
Error Value: 0x200001F8
Current Thread: main Id: 0x20005A70 Entry: 0x8009691 StackSize: 0x1000 StackMem: 0x200044D0 SP: 0x20005458
For more info, visit: https://mbed.com/s/error?error=0x80FF013D&tgt=NUCLEO_F767ZI
– MbedOS Error Info –

= System will be rebooted due to a fatal error =
= Reboot count(=19) reached maximum, system will halt after rebooting =
++ MbedOS Fault Handler ++

FaultType: HardFault

Context:
R 0: 00000000
R 1: 20005484
R 2: 2000546C
R 3: 2000546D
R 4: 200054AC
R 5: 2000547C
R 6: 20005460
R 7: 2000546D
R 8: 20005484
R 9: 2000546C
R 10: 00000000
R 11: 00000000
R 12: 00000000
SP : 20005460
LR : 08008913
PC : 080043DC
xPSR : 21000000
PSP : 200053F8
MSP : 2007FFD0
CPUID: 411FC270
HFSR : 40000000
MMFSR: 00000000
BFSR : 00000000
UFSR : 00000100
DFSR : 00000000
AFSR : 00000000
Mode : Thread
Priv : Privileged
Stack: PSP

– MbedOS Fault Handler –

++ MbedOS Error Info ++
Error Status: 0x80FF013D Code: 317 Module: 255
Error Message: Fault exception
Location: 0x80043DC
Error Value: 0x200001F8
Current Thread: main Id: 0x20005A70 Entry: 0x8009691 StackSize: 0x1000 StackMem: 0x200044D0 SP: 0x20005460
For more info, visit: https://mbed.com/s/error?error=0x80FF013D&tgt=NUCLEO_F767ZI
– MbedOS Error Info –

= System will be rebooted due to a fatal error =
= Reboot count(=20) reached maximum, system will halt after rebooting =

Arm processors with a built in FPU will hard fault if you try to access a float or double that isn’t correctly byte aligned.
By having a packed struct with a char at the start you force all the subsequent fields to be misaligned. If you change your struct so the char is at the end not the start the problem will go away.

However if you then have an array of the struct the second instance may not be aligned correctly and so give you the same problem.

The simple solution is to add 3 bytes of padding to your structure so that it is always a neat length. This is exactly what the compiler would do if you don’t pack it but by explicitly defining the reserved bytes and then packing the struct you remove any ambiguity as to the final structure.

If for some reason you can’t change things to be aligned then you can cast the float to a int32_t, copy it to a local variable and then cast it back to being a float. That way the FPU doesn’t try to access it until it’s correctly aligned.

Thanks. Solved.