2023-12-13 12:26 AM - edited 2023-12-13 12:40 AM
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.
2023-12-13 01:01 AM - edited 2023-12-13 02:15 AM
Show the declarations of both structures. It may have something to do with 64-bit data present in one of them.
2023-12-13 01:11 AM
> 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
2023-12-13 01:13 AM
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?
2023-12-13 01:16 AM
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.
2023-12-13 01:38 AM - edited 2023-12-13 01:45 AM
@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 :clapping_hands::clapping_hands:) Removing this just ends up with the hard_Fault happening on a copy even earlier. No other instances of attributes to the structs.
2023-12-13 02:11 AM - edited 2023-12-13 02:15 AM
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.
2023-12-13 03:00 AM
Hello,
Just for your info, from PM0253:
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.
2023-12-13 03:00 AM
> 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
2023-12-13 03:00 AM
@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.