Lv_palette_main() generates a hard faut on Cortex M7 processor

Hi All

I have tried running a simple example (“Hello, World” centered on the screen) but I found that it always hard faults during lv_display_create() and I can trace it to this line

disp->theme = lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),
LV_THEME_DEFAULT_DARK, LV_FONT_DEFAULT);

The program flow is that it calls lv_palette_main() twice with parameters 5 and 0 (for the BLUE and RED palettes). With 5 is returns normally but with 0 it hard faults inside lv_palette_main().

It hard faults when returning the requested value: return colors[p];

Looking at the assembler code of the subroutine I see:

00023d20: cmp r0, #18
00023d22: sub sp, #8
00023d24: bls.n 0x23d2c
00023d26: add sp, #8
00023d28: b.w 0x22f30
00023d2c: add.w r0, r0, r0, lsl #1
00023d30: ldr r3, [pc, #36] @ (0x23d58)
00023d32: adds r2, r3, r0
00023d34: ldrh r3, [r3, r0] ← hard faults here
00023d36: movs r0, #0
00023d38: strh.w r3, [sp, #4]
00023d3c: ldrb.w r3, [sp, #4]
00023d40: bfi r0, r3, #0, #8
00023d44: ldrb.w r3, [sp, #5]
00023d48: bfi r0, r3, #8, #8
00023d4c: ldrb r3, [r2, #2]
00023d4e: bfi r0, r3, #16, #8
00023d52: add sp, #8
00023d54: bx lr

The reason is that it is trying to copy a misaligned long word at the instruction shown.

The issue being that the colors is an array of bytes (each 3 making up GRB bytes)
static const lv_color_t colors = {
LV_COLOR_MAKE(0xF4, 0x43, 0x36), LV_COLOR_MAKE(0xE9, 0x1E, 0x63), LV_COLOR_MAKE(0x9C, 0x27, 0xB0), LV_COLOR_MAKE(0x67, 0x3A, 0xB7),
LV_COLOR_MAKE(0x3F, 0x51, 0xB5), LV_COLOR_MAKE(0x21, 0x96, 0xF3), LV_COLOR_MAKE(0x03, 0xA9, 0xF4), LV_COLOR_MAKE(0x00, 0xBC, 0xD4),
LV_COLOR_MAKE(0x00, 0x96, 0x88), LV_COLOR_MAKE(0x4C, 0xAF, 0x50), LV_COLOR_MAKE(0x8B, 0xC3, 0x4A), LV_COLOR_MAKE(0xCD, 0xDC, 0x39),
LV_COLOR_MAKE(0xFF, 0xEB, 0x3B), LV_COLOR_MAKE(0xFF, 0xC1, 0x07), LV_COLOR_MAKE(0xFF, 0x98, 0x00), LV_COLOR_MAKE(0xFF, 0x57, 0x22),
LV_COLOR_MAKE(0x79, 0x55, 0x48), LV_COLOR_MAKE(0x60, 0x7D, 0x8B), LV_COLOR_MAKE(0x9E, 0x9E, 0x9E)
};

The compiler is accessing the entry as a long word access before removing the unwanted byte for the return (or maybe moving the content into position).

When the subroutine’s parameter is 5 it happens to be on a long word boundary but with 0 it isn’t. In my case the compiler has put the start of the array at 0x42667 (in memory), which I also see in the map file:

.rodata.colors.2
0x00042667 0x39 Build/lv_palette.o

This tells me that the code is not safe for processors that don’t natively support uneven access or it is necessary to configure the compiler to avoid such pointer accesses when compiling this file.

Someone once told me that the GCC compile option “-mno-unaligned-access” can be used to ensure that no misaligned accesses take place so I tried it on the file in question. The assembler code was subsequently different and included a call to memset() from the compiler’s own library in it. However it still crashed (this time with the parameter 5) - but now in the memset() sub-routine called by the lv_palette_main() code as constructed by the compiler.
Possibly it needs the compile option for all files (including the run time library builds)?

What is intriguing for me is that the rest of the project code (with is much larger than the LVGL code, including FAT file system, USB class stacks and TCP/IP with TLS layers) doesn’t have any issues when build with standard GCC compile options.

Am I the only one with issues when building and running on a Cortex M7 and is there a standard workaround used to ensure that processors that don’t support misaligned word accesses can be used safely?

Regards

Mark

Hi

I have made a temporary workaround which fixes the hard fault - I had to do it in
lv_palette_main()
lv_palette_lighten()
and
lv_palette_darken()
as they all had the same problem.

  1. I declared the content of the struc lv_color_t to be volatile
    typedef struct {
    volatile uint8_t blue;
    volatile uint8_t green;
    volatile uint8_t red;
    } lv_color_t;

  2. In the routines I use an intermediate
    lv_color_t

to copy the individual entries to

lv_color_t value;
  1. The copy and return is as follows

    value.blue = colors[p][lvl].blue;
    ** value.red = colors[p][lvl].red;**
    ** value.green = colors[p][lvl].green;**

** return value;**

The volatile ensures that the compiler makes three bytes copies and so avoids any misaligned short or word copies as it was previously using for its return.

Now the example works and I get the “Hello World” in the middle of the screen on the HW without any hard faults.

My feeling was that it could be a GCC optimisation issue as I have it set to " -Os" for all files.

When I compile lv_palette.c with “-O0” and don’t make the above changes it unfortunately still hard faults, meaning that I haven’t yet been successful solving it with pure compiler settings and only have the code change solution at the moment, which will be a problem when updates to the LVGL library are used.

Can anyone explain how to correctly solve it?

Regards

Mark

Hi All

I revised the present workaround to keep it to the single file:

In the three functions instead of this return
return colors[p][lvl];
I have replaced them with
return fnSafeReturn(&colors[p][lvl]);

and added a local routine that ensures it stays safe.

static lv_color_t fnSafeReturn(const lv_color_t *ptr_lv_color)
{
volatile lv_color_t value; // use an intermediate struct to copy potentially misaligned data to

value.blue = ptr_lv_color->blue;                                     // force three byte copies (avoiding potential hard fault if the compiler tries to use longer widths on misaligned addresses)
value.red = ptr_lv_color->red;
value.green = ptr_lv_color->green;

return value;

}

This also allows the HW to operate.

Would be grateful for comments or advice on avoiding code changes.

Regards

Mark