mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +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 "cpu.h"
|
||||||
#include "periph/flashpage.h"
|
#include "periph/flashpage.h"
|
||||||
|
#include "unaligned.h"
|
||||||
|
|
||||||
#define ENABLE_DEBUG 0
|
#define ENABLE_DEBUG 0
|
||||||
#include "debug.h"
|
#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
|
/* 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.
|
* 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;
|
const uint8_t *data = _data;
|
||||||
union {
|
union {
|
||||||
uint32_t u32;
|
uint32_t u32;
|
||||||
@ -169,13 +174,13 @@ static uint32_t unaligned_pad_start(const void *_data, uint8_t len)
|
|||||||
|
|
||||||
switch (len) {
|
switch (len) {
|
||||||
case 3:
|
case 3:
|
||||||
buffer.u8[1] = *data++;
|
buffer.u8[offset + len - 3] = *data++;
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
case 2:
|
case 2:
|
||||||
buffer.u8[2] = *data++;
|
buffer.u8[offset + len - 2] = *data++;
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
case 1:
|
case 1:
|
||||||
buffer.u8[3] = *data++;
|
buffer.u8[offset + len - 1] = *data++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.u32;
|
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))
|
static void _write_page(void* dst, const void *data, size_t len, void (*cmd_write)(void))
|
||||||
{
|
{
|
||||||
/* set bytes in the first, unaligned word */
|
/* set bytes in the first, unaligned word */
|
||||||
uint8_t unaligned_start = (4 - ((uintptr_t)dst & 0x3)) & 0x3;
|
uint8_t offset_unaligned_start = (uintptr_t)dst & 0x3;
|
||||||
len -= unaligned_start;
|
/* 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 */
|
/* set bytes in the last, unaligned word */
|
||||||
uint8_t unaligned_end = len & 0x3;
|
uint8_t len_unaligned_end = len & 0x3;
|
||||||
len -= unaligned_end;
|
len -= len_unaligned_end;
|
||||||
|
|
||||||
/* word align destination address */
|
/* word align destination address */
|
||||||
uint32_t *dst32 = (void*)((uintptr_t)dst & ~0x3);
|
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();
|
_cmd_clear_page_buffer();
|
||||||
|
|
||||||
/* write the first, unaligned bytes */
|
/* write the first, unaligned bytes */
|
||||||
if (unaligned_start) {
|
if (len_unaligned_start) {
|
||||||
*dst32++ = unaligned_pad_start(data, unaligned_start);
|
*dst32++ = unaligned_pad_start(data, len_unaligned_start,
|
||||||
data = (uint8_t*)data + unaligned_start;
|
offset_unaligned_start);
|
||||||
|
data = (void *)((uintptr_t)data + len_unaligned_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy whole words */
|
/* copy whole words */
|
||||||
const uint32_t *data32 = data;
|
|
||||||
while (len) {
|
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);
|
len -= sizeof(uint32_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write the last, unaligned bytes */
|
/* write the last, unaligned bytes */
|
||||||
if (unaligned_end) {
|
if (len_unaligned_end) {
|
||||||
*dst32 = unaligned_pad_end(data32, unaligned_end);
|
*dst32 = unaligned_pad_end(data, len_unaligned_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_write();
|
cmd_write();
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "od.h"
|
#include "od.h"
|
||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
#include "periph/flashpage.h"
|
#include "periph/flashpage.h"
|
||||||
|
#include "unaligned.h"
|
||||||
|
|
||||||
#define LINE_LEN (16)
|
#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)
|
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) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
|
|
||||||
const uint16_t single_data = 0x1234;
|
const uint16_t single_data = 0x1234;
|
||||||
const uint8_t test_data[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE };
|
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]");
|
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 */
|
/* reset aux page */
|
||||||
for (uint32_t i = 0; i < FLASH_USER_PAGE_AUX_SIZE; ++i) {
|
sam0_flashpage_aux_reset(NULL);
|
||||||
if (*(uint8_t*)sam0_flashpage_aux_get(i) != 0xFF) {
|
|
||||||
printf("user page not cleared at offset 0x%"PRIx32"\n", i);
|
/* 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;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* write test data */
|
/* check if test data was written correctly */
|
||||||
sam0_flashpage_aux_write(dst, test_data, sizeof(test_data));
|
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);
|
||||||
/* write single half-word */
|
return -1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
puts("[SUCCESS]");
|
puts("[SUCCESS]");
|
||||||
|
Loading…
Reference in New Issue
Block a user