From 13670be9bbbbd0469ab83858021564dc6a59fa96 Mon Sep 17 00:00:00 2001 From: Daniel Lockau Date: Tue, 5 Jan 2021 12:47:50 +0100 Subject: [PATCH 1/2] tests/periph_flashpage: test unaligned write for sam0 Adds test of unaligned write to already integrated sam0_common config area test. --- tests/periph_flashpage/main.c | 64 ++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/tests/periph_flashpage/main.c b/tests/periph_flashpage/main.c index f78921caef..1a42f60d7e 100644 --- a/tests/periph_flashpage/main.c +++ b/tests/periph_flashpage/main.c @@ -26,6 +26,7 @@ #include "od.h" #include "shell.h" #include "periph/flashpage.h" +#include "unaligned.h" #define LINE_LEN (16) @@ -552,42 +553,57 @@ static int cmd_dump_config(int argc, char **argv) static int cmd_test_config(int argc, char **argv) { + /* This test is sam0 specific and also tests + * the unaligned writes for the sam0 flashpage + * driver implementation + */ + (void) argc; (void) argv; const uint16_t single_data = 0x1234; const uint8_t test_data[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE }; - uint32_t dst = FLASH_USER_PAGE_AUX_SIZE - (sizeof(test_data) + 2); + + assert(((int32_t)FLASH_USER_PAGE_AUX_SIZE - (int32_t)(sizeof(test_data) + 2 + 3)) > 0); puts("[START]"); - sam0_flashpage_aux_reset(NULL); + for (uint32_t dst_offset = 0; dst_offset < 4; dst_offset++) { + /* destination base at 4 byte aligned address */ + uint32_t dst = (uint32_t)(FLASH_USER_PAGE_AUX_SIZE + - (sizeof(test_data) + 2 + 3)) & ~((uint32_t)0x3); + /* add data destination offset */ + dst += dst_offset; - /* check if the AUX page has been cleared */ - for (uint32_t i = 0; i < FLASH_USER_PAGE_AUX_SIZE; ++i) { - if (*(uint8_t*)sam0_flashpage_aux_get(i) != 0xFF) { - printf("user page not cleared at offset 0x%"PRIx32"\n", i); + /* reset aux page */ + sam0_flashpage_aux_reset(NULL); + + /* check if the AUX page has been cleared */ + for (uint32_t i = 0; i < FLASH_USER_PAGE_AUX_SIZE; ++i) { + if (*(uint8_t*)sam0_flashpage_aux_get(i) != 0xFF) { + printf("dst_offset=%"PRIu32": user page not cleared at offset 0x%"PRIx32"\n", dst_offset, i); + return -1; + } + } + + /* write test data */ + sam0_flashpage_aux_write(dst, test_data, sizeof(test_data)); + + /* write single half-word */ + sam0_flashpage_aux_write(dst + sizeof(test_data), &single_data, sizeof(single_data)); + + /* check if half-word was written correctly */ + uint16_t data_in = unaligned_get_u16(sam0_flashpage_aux_get(dst + sizeof(test_data))); + if (data_in != single_data) { + printf("dst_offset=%"PRIu32": %x != %x, offset = 0x%"PRIx32"\n", dst_offset, single_data, data_in, dst + sizeof(test_data)); return -1; } - } - /* write test data */ - sam0_flashpage_aux_write(dst, test_data, sizeof(test_data)); - - /* write single half-word */ - sam0_flashpage_aux_write(dst + sizeof(test_data), &single_data, sizeof(single_data)); - - /* check if half-word was written correctly */ - uint16_t data_in = *(uint16_t*)sam0_flashpage_aux_get(dst + sizeof(test_data)); - if (data_in != single_data) { - printf("%x != %x, offset = 0x%"PRIx32"\n", single_data, data_in, dst + sizeof(test_data)); - return -1; - } - - /* check if test data was written correctly */ - if (memcmp(sam0_flashpage_aux_get(dst), test_data, sizeof(test_data))) { - printf("write test_data failed, offset = 0x%"PRIx32"\n", dst); - return -1; + /* check if test data was written correctly */ + if (memcmp(sam0_flashpage_aux_get(dst), test_data, sizeof(test_data))) { + printf("dst_offset=%"PRIu32": write test_data failed, offset = 0x%"PRIx32"\n", dst_offset, dst); + return -1; + } } puts("[SUCCESS]"); From ef7cbdb31292565211c539528317a4abe2ef86b4 Mon Sep 17 00:00:00 2001 From: Daniel Lockau Date: Tue, 5 Jan 2021 12:49:26 +0100 Subject: [PATCH 2/2] cpu/sam0_common/periph/flashpage: fix unaligned writes --- cpu/sam0_common/periph/flashpage.c | 42 +++++++++++++++++++----------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/cpu/sam0_common/periph/flashpage.c b/cpu/sam0_common/periph/flashpage.c index 69184695ea..a7fd478812 100644 --- a/cpu/sam0_common/periph/flashpage.c +++ b/cpu/sam0_common/periph/flashpage.c @@ -31,6 +31,7 @@ #include "cpu.h" #include "periph/flashpage.h" +#include "unaligned.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -158,9 +159,13 @@ static void _cmd_write_page(void) /* We have to write whole words, but writing 0xFF is basically a no-op * so fill the unaligned bytes with 0xFF to get a whole extra word. + * To support writes of data with less than 4 bytes, an offset needs + * to be supplied. */ -static uint32_t unaligned_pad_start(const void *_data, uint8_t len) +static uint32_t unaligned_pad_start(const void *_data, uint8_t len, uint8_t offset) { + assert((4 - offset) >= len); + const uint8_t *data = _data; union { uint32_t u32; @@ -169,13 +174,13 @@ static uint32_t unaligned_pad_start(const void *_data, uint8_t len) switch (len) { case 3: - buffer.u8[1] = *data++; + buffer.u8[offset + len - 3] = *data++; /* fall-through */ case 2: - buffer.u8[2] = *data++; + buffer.u8[offset + len - 2] = *data++; /* fall-through */ case 1: - buffer.u8[3] = *data++; + buffer.u8[offset + len - 1] = *data++; } return buffer.u32; @@ -209,12 +214,14 @@ static uint32_t unaligned_pad_end(const void *_data, uint8_t len) static void _write_page(void* dst, const void *data, size_t len, void (*cmd_write)(void)) { /* set bytes in the first, unaligned word */ - uint8_t unaligned_start = (4 - ((uintptr_t)dst & 0x3)) & 0x3; - len -= unaligned_start; + uint8_t offset_unaligned_start = (uintptr_t)dst & 0x3; + /* use MIN to support short data sizes below 3 bytes */ + uint8_t len_unaligned_start = MIN((4 - offset_unaligned_start) & 0x3, len); + len -= len_unaligned_start; /* set bytes in the last, unaligned word */ - uint8_t unaligned_end = len & 0x3; - len -= unaligned_end; + uint8_t len_unaligned_end = len & 0x3; + len -= len_unaligned_end; /* word align destination address */ uint32_t *dst32 = (void*)((uintptr_t)dst & ~0x3); @@ -223,21 +230,26 @@ static void _write_page(void* dst, const void *data, size_t len, void (*cmd_writ _cmd_clear_page_buffer(); /* write the first, unaligned bytes */ - if (unaligned_start) { - *dst32++ = unaligned_pad_start(data, unaligned_start); - data = (uint8_t*)data + unaligned_start; + if (len_unaligned_start) { + *dst32++ = unaligned_pad_start(data, len_unaligned_start, + offset_unaligned_start); + data = (void *)((uintptr_t)data + len_unaligned_start); } /* copy whole words */ - const uint32_t *data32 = data; while (len) { - *dst32++ = *data32++; + /* due to unknown input data alignment and the conditional + * shift applied above, data might not be aligned to a 4 byte + * boundary at this point + */ + *dst32++ = unaligned_get_u32(data); + data = (void *)((uintptr_t)data + sizeof(uint32_t)); len -= sizeof(uint32_t); } /* write the last, unaligned bytes */ - if (unaligned_end) { - *dst32 = unaligned_pad_end(data32, unaligned_end); + if (len_unaligned_end) { + *dst32 = unaligned_pad_end(data, len_unaligned_end); } cmd_write();