From aecf9049782835fc28dff1341cc0910a7305816e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 2 Jul 2026 17:01:08 +0200 Subject: [PATCH] hal: advance source index in aligned flash-write fast path The bulk FLASH_Program path read data+w but never advanced w, so a partial-word tail after an aligned run re-read the input from offset 0. Affects kinetis, mcxa, mcxw. Pin with an mcxa bulk+tail test case. --- hal/kinetis.c | 1 + hal/mcxa.c | 1 + hal/mcxw.c | 1 + tools/unit-tests/unit-flash-write-mcxa.c | 26 ++++++++++++++++++++++++ 4 files changed, 29 insertions(+) diff --git a/hal/kinetis.c b/hal/kinetis.c index c0f86c5b..91d83d47 100644 --- a/hal/kinetis.c +++ b/hal/kinetis.c @@ -337,6 +337,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) ret = FLASH_Program(&pflash, address, (uint8_t*)data + w, len_align); if (ret != kStatus_FTFx_Success) return -1; + w += len_align; len -= len_align; address += len_align; } diff --git a/hal/mcxa.c b/hal/mcxa.c index f1dd73c9..1f819103 100644 --- a/hal/mcxa.c +++ b/hal/mcxa.c @@ -101,6 +101,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) ret = FLASH_ProgramPhrase(&pflash, address, (uint8_t*)data + w, len_align); if (ret != kStatus_Success) return -1; + w += len_align; len -= len_align; address += len_align; } diff --git a/hal/mcxw.c b/hal/mcxw.c index 0d3d352a..776c0ecf 100644 --- a/hal/mcxw.c +++ b/hal/mcxw.c @@ -183,6 +183,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) write_flash_qword((uint32_t *)(address + i), (const uint32_t *)(data + w + i)); } + w += len_align; len -= len_align; address += len_align; } diff --git a/tools/unit-tests/unit-flash-write-mcxa.c b/tools/unit-tests/unit-flash-write-mcxa.c index 29eaa2f8..b98c2606 100644 --- a/tools/unit-tests/unit-flash-write-mcxa.c +++ b/tools/unit-tests/unit-flash-write-mcxa.c @@ -136,6 +136,31 @@ START_TEST(test_unaligned_write_single_word) } END_TEST +/* An aligned write longer than one flash word takes the bulk fast path + * (FLASH_ProgramPhrase over data + w) for the first 16 bytes, then a + * partial-word tail for the rest. The fast path must advance the data source + * index "w" by the bulk length; if it does not, the tail re-reads the input + * from the start and programs the wrong bytes. Write 24 bytes at an aligned + * address: mock_flash[0..23] must equal the input. Before the fix, + * mock_flash[16..23] held data[0..7] instead of data[16..23]. */ +START_TEST(test_aligned_write_bulk_then_tail) +{ + uint8_t data[24]; + int i; + uint32_t base = (uint32_t)(uintptr_t)mock_flash; + + for (i = 0; i < 24; i++) + data[i] = (uint8_t)(i + 1); + + ck_assert_int_eq(hal_flash_write(base, data, 24), 0); + + for (i = 0; i < 24; i++) + ck_assert_uint_eq(mock_flash[i], data[i]); + for (i = 24; i < MOCK_FLASH_SIZE; i++) + ck_assert_uint_eq(mock_flash[i], 0xFF); +} +END_TEST + Suite *flash_write_suite(void) { Suite *s = suite_create("flash-write-mcxa"); @@ -144,6 +169,7 @@ Suite *flash_write_suite(void) tcase_add_checked_fixture(tc, setup, teardown); tcase_add_test(tc, test_unaligned_write_spanning_two_words); tcase_add_test(tc, test_unaligned_write_single_word); + tcase_add_test(tc, test_aligned_write_bulk_then_tail); suite_add_tcase(s, tc); return s;