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:
commit
1a30a3a280
@ -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();
|
||||
|
@ -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]");
|
||||
|
Loading…
Reference in New Issue
Block a user