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
gbm
Lead III

Show the declarations of both structures. It may have something to do with 64-bit data present in one of them.

> Show the declarations of both structures.

Not only that, but also location in memory (possibly the mapfile), and disasm of the relevant portion of code.

The compiler has decided to use load double (as unaligned 32-bit accesses would be OK), but, as you've said:

> compilers job to add the desired padding to the data to have it aligned

so there's probably some switch or attribute which misguided the compiler.

JW

RBrec.1
Associate III

Kinda easier said than done.. Initially structName_t was

typedef struct {
 int32_t field1;
 int32_t field2;
 int32_t field3;
 int32_t field4;
 int32_t field5;
 int32_t field6;
}structName_t;

 Which didn't work, so under debugging I found that if even one of them was 32-bit, it failed. Which one doesn't matter. The Signal_t struct however is a mess and would require a lot of rewriting, as it is kinda sensitive information. Obviously hard to help if I throw that at you, but it does include a bunch of sub-structs that again includes 8, 16, 32 bits and a lot of enums. Either of the enums have values above 32-bits, so I assume the size of the fields then would have a max size of 32? Could you maybe elaborate what you mean by 64-bit data present somewhere?

gbm
Lead III

The problem may arise if the other structure has some packing options or 64-bit data, so we cannot proceed without the other structure declaration.

RBrec.1
Associate III

@gbm @waclawek.jan  I am currently looking into how chaos such a file would be, as the struct is quite large. In the meantime, I notice there are some structs that has the following struct

 

 

typedef enum {
 ..
 ..
 LAST = 124,
} Enum_t;

typedef struct
{
    Enum_e enum;
    uint8_t config1;
    uint8_t config2;
    uint8_t config3;
    uint32_t config4;
    uint8_t config5;
} SubConfig_t;

#define NUMBER_OF_SUB_CONFIG 10

typedef struct
{
    SubConfig_t config[NUMBER_OF_SUB_CONFIG];
} Config_t;

 

 

where Config_t is a field in Signal_t. This seems to be the most complex field of Signal_t. Just wonder if the list is somehow messing with data alignment?

The unaligned issue seems to have been a problem earlier, as one other struct also has the packed attribute (Couldn't get any other explanation on the problem at the time other than that the packed structure worked 👏👏)  Removing this just ends up with the hard_Fault happening on a copy even earlier. No other instances of attributes to the structs. 

gbm
Lead III

The probable source of the problem is that structure with the packed attribute. With GCC, "packed" is not exactly what we usually assume it to be. GCC "packed" structure is not really packed; it's only shortened at the end.

Your quick fix caused the compiler to abandon the idea of using long, aligned accesses for structure copy - that's why it worked. It didn't fix the problem, it just masked it.

SofLit
ST Employee

Hello,

Just for your info, from PM0253:

SofLit_0-1702464790002.png

So look at your generated assembly code and check if there is no instruction outside the list above then check the UNALIGN_TRP bit in SCB_CCR register. If it's set, reset it.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

> With GCC, "packed" is not exactly what we usually assume it to be. GCC "packed" structure is not really packed; it's only shortened at the end.

I've never heard of this, and my experience is different, individual members of non-packed structs being word-aligned (i.e. appropriate spacers inserted by gcc).

#include <stdint.h>

struct {
  uint8_t byte;
  uint16_t halfword;
  uint32_t word;
} a;

struct __attribute__((packed)) {
  uint8_t byte;
  uint16_t halfword;
  uint32_t word;
} b;

volatile uint32_t x;

int main(void) {
  a.byte = x;
  a.halfword = x;
  a.word = x;

  b.byte = x;
  b.halfword = x;
  b.word = x;

  while(1);
}
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.8.3 20131129 (release) [ARM/embedded-4_8-branch revision 205641]
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Compiling: dl.c
d:/PROGRA~1/ARMTools/launchpad.net_gcc-arm-embedded/bin/arm-none-eabi-gcc -c -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mthumb  -I. -gdwarf-2 -DROM_RUN -DSTM32F407xx   -O3 -Wall -Wstrict-prototypes -Wcast-align -Wcast-qual -Wimplicit -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wswitch -Wredundant-decls -Wreturn-type -Wshadow -Wstrict-prototypes -Wunused -Wa,-adhlns=objdir/dl.lst -ILibraries/STM32F4xx_StdPeriph_Driver/inc/ -std=gnu99 -g3 -MD -MP -MF .dep/dl.o.d dl.c -o objdir/dl.o

Linking: dl.elf
d:/PROGRA~1/ARMTools/launchpad.net_gcc-arm-embedded/bin/arm-none-eabi-gcc -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16  -I. -gdwarf-2 -DROM_RUN -DSTM32F407xx   -O3 -Wall -Wstrict-prototypes -Wcast-align -Wcast-qual -Wimplicit -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wswitch -Wredundant-decls -Wreturn-type -Wshadow -Wstrict-prototypes -Wunused -Wa,-adhlns=objdir/startup_stm32f4xx.o -ILibraries/STM32F4xx_StdPeriph_Driver/inc/ -std=gnu99 -g3 -MD -MP -MF .dep/dl.elf.d objdir/startup_stm32f4xx.o objdir/dl.o --output dl.elf -nostartfiles -Wl,-Map=dl.map,--cref -lm -l:stm32f4_stdlib.a -Tstm32_flash.ld
int main(void) {
  a.byte = x;
 8000204:	4b09      	ldr	r3, [pc, #36]	; (800022c <main+0x28>)
 8000206:	490a      	ldr	r1, [pc, #40]	; (8000230 <main+0x2c>)
  a.halfword = x;
  a.word = x;

  b.byte = x;
 8000208:	4a0a      	ldr	r2, [pc, #40]	; (8000234 <main+0x30>)
  uint32_t word;
} b;

volatile uint32_t x;

int main(void) {
 800020a:	b4f0      	push	{r4, r5, r6, r7}
  a.byte = x;
 800020c:	681f      	ldr	r7, [r3, #0]
  a.halfword = x;
 800020e:	681e      	ldr	r6, [r3, #0]
  a.word = x;
 8000210:	6818      	ldr	r0, [r3, #0]

  b.byte = x;
 8000212:	681d      	ldr	r5, [r3, #0]
  b.halfword = x;
 8000214:	681c      	ldr	r4, [r3, #0]
  b.word = x;
 8000216:	681b      	ldr	r3, [r3, #0]
} b;

volatile uint32_t x;

int main(void) {
  a.byte = x;
 8000218:	700f      	strb	r7, [r1, #0]
  a.halfword = x;
 800021a:	804e      	strh	r6, [r1, #2]
  a.word = x;
 800021c:	6048      	str	r0, [r1, #4]

  b.byte = x;
 800021e:	7015      	strb	r5, [r2, #0]
  b.halfword = x;
 8000220:	f8a2 4001 	strh.w	r4, [r2, #1]
  b.word = x;
 8000224:	f8c2 3003 	str.w	r3, [r2, #3]
 8000228:	e7fe      	b.n	8000228 <main+0x24>

 

JW

RBrec.1
Associate III

@gbm Not sure if I understand you now. So the struct in mind (the only packed one to begin with) is the SubConfig_t struct below. Having this declared with the packed attribute makes stuff work. Removing it, i.e. no packed structs left and the problem occurs here. 

typedef enum {
 ..
 ..
 LAST = 124,
} Enum_t;

typedef struct
{
    Enum_e enum;
    uint8_t config1;
    uint8_t config2;
    uint8_t config3;
    uint32_t config4;
    uint8_t config5;
} SubConfig_t;

#define NUMBER_OF_SUB_CONFIG 10

typedef struct
{
    SubConfig_t subConfig[NUMBER_OF_SUB_CONFIG];
} Config_t;

typedef struct
{
    Config_t config;
} Signal_t;

static void doSomething(const SubConfig_t *sendData, Signal_t *signal)
{
  for (int i = 0; i < NUMBER_OF_SUB_CONFIG; i++)
  {
     signal->config.subconfig[i] = *sendData; // <--- Hard fault occurs
     break;
  }
  ...
}

 Again, the code above fails as is. Having SubConfig_t declared as packed make it work. Clearly I'm new to these kind of memory issues, but I cant seem to understand why it should fail in the first place.