fix(mem): align TLSF to 8 bytes on ARMv7-M/E-M/v8-M to avoid LDRD fault#10015
fix(mem): align TLSF to 8 bytes on ARMv7-M/E-M/v8-M to avoid LDRD fault#10015KentLee86 wants to merge 1 commit intolvgl:masterfrom
Conversation
On 32-bit ARM Cortex-M3 / M4 / M7 / M33 (ARMv7-M, ARMv7E-M, ARMv8-M) the LDRD / STRD instructions require strict 8-byte alignment and fault with a UsageFault (UFSR.UNALIGNED) regardless of CCR.UNALIGN_TRP. GCC emits these for 64-bit struct accesses assuming malloc returns pointers aligned to alignof(max_align_t), which is 8 on these targets (double / long long). The TLSF built-in allocator currently uses 4-byte alignment for 32-bit builds, so block addresses can end up only 4-byte aligned. In internal SRAM this usually slips by because allocations tend to land on 8-byte boundaries anyway, but on external RAM pools (e.g. STM32H7 + FMC SDRAM configured via LV_MEM_ADR) the mis-aligned addresses are hit quickly and the CPU hard-faults inside lv_tlsf_malloc / lv_draw_rect on the first non-trivial allocation. Detect LDRD-capable ARM M-profile cores via __ARM_ARCH >= 7 (excluding ARMv6-M, which has no LDRD) and promote ALIGN_SIZE_LOG2 to 3 (8 bytes) there. No behavior change on AVR / RV32 / ARMv6-M / other 32-bit targets, and TLSF_64BIT still takes precedence. Related: lvgl#4747 (unaligned hardfault, closed as not planned), LVGL forum "Getting hardfault when using Ext. RAM for LV_MEM_ADR" and multiple STM32H7 + LVGL reports in the ST community. Verified on STM32H743II + 32 MB SDRAM: LV_MEM_ADR in SDRAM (4 MB) now runs a 8-row 800x480 dashboard cleanly; previously HardFault on the first lv_draw_rect. Signed-off-by: wslee <dldntjr407@gmail.com>
|
On second thought, here's how a maintainer can verify the root cause without any external hardware or reproducer project — just by inspecting an existing ARMv7-M LVGL build artifact. Verify GCC does emit LDRD on malloc-returned pointersTake any existing LVGL build for Cortex-M3 / M4 / M7 (for example the arm-none-eabi-objdump -d firmware.elf \
| awk '/^[0-9a-f]+ <lv_draw_rect>/,/^$/' \
| grep -E 'ldrd|strd' | headYou will see LDRD/STRD instructions inside Why TLSF's current 4-byte alignment is UB on these targetsPer ARMv7-M Architecture Reference Manual (§A3.2.1),
TLSF with So the fix in this PR isn't "add a workaround for STM32H7" — it's restoring the alignment contract that the C standard already requires of any general-purpose allocator on these targets. The same latent UB exists on M3 / M4 / M33 but just happens to rarely hit the wrong byte boundary. Net effect of the change
|
|
Hi 👋, thank you for your PR! We've run benchmarks in an emulated environment. Here are the results: ARM Emulated 32b - lv_conf_perf32b
Detailed Results Per Scene
ARM Emulated 64b - lv_conf_perf64b
Detailed Results Per Scene
Disclaimer: These benchmarks were run in an emulated environment using QEMU with instruction counting mode. 🤖 This comment was automatically generated by a bot. |
kisvegabor
left a comment
There was a problem hiding this comment.
It can explain a lot of weird issues. Thank you for investigating it.
The changes are ok from my side. Let's wait for @AndreCostaaa's opinion too
Fixes #4747 (
unaligned memory access, previously closed as not planned — see below).Summary
On 32-bit ARM Cortex-M3 / M4 / M7 / M33 (ARMv7-M, ARMv7E-M, ARMv8-M) the
LDRD/STRDinstructions require strict 8-byte alignment and fault with a UsageFault (UFSR.UNALIGNED) regardless ofCCR.UNALIGN_TRP. GCC emits these for 64-bit struct accesses assumingmallocreturns pointers aligned toalignof(max_align_t)— which is 8 on these targets becausedouble/long longare 8-byte aligned.The built-in TLSF allocator currently uses
ALIGN_SIZE_LOG2 = 2(4 bytes) on all 32-bit builds. In internal SRAM allocations usually happen to land on 8-byte boundaries and the issue stays hidden, but on external RAM pools (e.g. STM32H7 + FMC SDRAM configured viaLV_MEM_ADR) the mis-aligned addresses are hit almost immediately and the CPU hard-faults insidelv_tlsf_malloc→lv_mem_core_builtin.c:147during the first non-trivial allocation from the draw path.Change
Detect LDRD-capable M-profile cores via
__ARM_ARCH >= 7(excluding ARMv6-M, which has no LDRD) and promoteALIGN_SIZE_LOG2to 3 (8 bytes) there.TLSF_64BITstill takes precedence. No behavior change on AVR, RV32, ARMv6-M (Cortex-M0/M0+) or other 32-bit targets.How it was diagnosed
Reproducer:
[env:lvgl_test]on a STM32H743II board with 32 MB FMC SDRAM,LV_MEM_ADR = 0xC0100000(4 MB pool in SDRAM free region after the 800×480 RGB565 LTDC framebuffer).lv_init()succeeds butlv_timer_handler()hard-faults on the firstlv_draw_rect.GDB attach after fault (J-Link):
Ruled out by direct tests before landing on TLSF:
{u32, u16, u8, void*, u64}at0xC0100000reads/writes fine. A 25 MB pattern R/W stress alongside LVGL runs 401 iterations with 0 errors.CCR.UNALIGN_TRP: already0, clearing it explicitly has no effect (LDRD strict alignment is ISA-level, independent of that bit).Workaround while the fix is not merged: build LVGL with
-mno-unaligned-accessso GCC stops emitting LDRD/STRD. This also fixes the symptom but doesn't address the root cause (TLSF's 4-byte alignment is belowalignof(max_align_t)on these targets).Verification
Same board, same env, only the diff in this PR applied (no
-mno-unaligned-access):lv_init()✓,lv_timer_handler()✓Notes
#elifblock matching the existing#if defined (TLSF_64BIT)style, no reformatting.lv_conf_template.h→lv_conf_internal_gen.py/ Kconfig not affected.TLSF_64BIT), and adding an ARMv7-M HW test would require CI changes. Happy to add one if that's the project's preference.Related reports
unaligned memory access, same symptom, closed as not plannedMarked as Draft for maintainer feedback on the detection macro and whether an additional conditional (e.g. also
__ARM_FEATURE_LDRD) is preferred.