1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #15707 from ML-PA-Consulting-GmbH/fix/20210105__sam0_common-periph-flashpage__fix_unaligned_write

sam0_common/periph/flashpage: fix unaligned write
This commit is contained in:
benpicco 2021-01-22 20:24:53 +01:00 committed by GitHub
commit 1a30a3a280
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 39 deletions

View File

@ -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();

View File

@ -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]");