-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Description
cpu/sam0_common/periph/flashpage.c
bit error on read, causing data corruption on same54-xpro.
Data stored on the flashpage seems to get corrupted under certain circumstances, when it is written twice.
An ECC interrupt is triggered when a bit flips. (datasheet 25.6.12.2 ECC Error Detection)
Steps to reproduce the issue
I observed for FlashDB, that it is for example overwriting a value of 0x7F with 0x3F, which should not cause problems for flash.
But sometimes when this happens, the written byte is corrupted.
- apply this patch to be able to write raw bytes in the periph flashpage test:
diff --git a/tests/periph/flashpage/main.c b/tests/periph/flashpage/main.c
index d152b305a4..bc633ff475 100644
--- a/tests/periph/flashpage/main.c
+++ b/tests/periph/flashpage/main.c
@@ -23,11 +23,13 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <ctype.h>
#include "od.h"
#include "shell.h"
#include "periph/flashpage.h"
#include "unaligned.h"
+#include "fmt.h"
#define LINE_LEN (16)
@@ -44,7 +46,7 @@
/*
* @brief Allocate an aligned buffer for raw writings
*/
-static char raw_buf[RAW_BUF_SIZE] ALIGNMENT_ATTR;
+static uint8_t raw_buf[RAW_BUF_SIZE] ALIGNMENT_ATTR;
#ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE
/**
@@ -219,7 +221,7 @@ static int cmd_write_raw(int argc, char **argv)
#endif
if (argc < 3) {
- printf("usage: %s <addr> <data>\n", argv[0]);
+ printf("usage: %s <addr> <hexadecimal data>\n", argv[0]);
return 1;
}
@@ -229,15 +231,27 @@ static int cmd_write_raw(int argc, char **argv)
addr = getaddr(argv[1]);
#endif
/* try to align */
- memcpy(raw_buf, argv[2], strlen(argv[2]));
+ int len;
+ if ((len = strlen(argv[2])) % 2 || (unsigned)len > sizeof(raw_buf) * 2) {
+ printf("error: data must have an even length and must be <= %u\n",
+ sizeof(raw_buf) * 2);
+ return 1;
+ }
+ for (int i = 0; i < len; i++) {
+ if (!isxdigit((int)(argv[2][i]))) {
+ printf("error: data must be hexadecimal\n");
+ return 1;
+ }
+ }
+ len = fmt_hex_bytes(raw_buf, argv[2]);
- flashpage_write((void*)addr, raw_buf, strlen(raw_buf));
+ flashpage_write((void*)addr, raw_buf, len);
#if (__SIZEOF_POINTER__ == 2)
printf("wrote local data to flash address %#" PRIx16 " of len %u\n",
- addr, strlen(raw_buf));
+ addr, len);
#else
printf("wrote local data to flash address %#" PRIx32 " of len %u\n",
- addr, strlen(raw_buf));
+ addr, len);
#endif
return 0;
}
@@ -423,7 +437,7 @@ static int cmd_test_last_raw(int argc, char **argv)
(void) argv;
unsigned last_free = flashpage_last_free();
- memset(raw_buf, 0, sizeof(raw_buf));
+ memset(raw_buf, 0xff, sizeof(raw_buf));
/* try to align */
memcpy(raw_buf, "test12344321tset", 16);
@@ -437,7 +451,7 @@ static int cmd_test_last_raw(int argc, char **argv)
flashpage_write(flashpage_addr(last_free), raw_buf, sizeof(raw_buf));
/* verify that previous write_raw effectively wrote the desired data */
- if (memcmp(flashpage_addr(last_free), raw_buf, strlen(raw_buf)) != 0) {
+ if (memcmp(flashpage_addr(last_free), raw_buf, 16) != 0) {
puts("error verifying the content of last page");
return 1;
}
@@ -584,10 +598,10 @@ static int cmd_test_last_rwwee_raw(int argc, char **argv)
/* erase the page first */
flashpage_rwwee_write_page(((int)FLASHPAGE_RWWEE_NUMOF - 1), NULL);
- flashpage_rwwee_write(flashpage_rwwee_addr((int)FLASHPAGE_RWWEE_NUMOF - 1), raw_buf, strlen(raw_buf));
+ flashpage_rwwee_write(flashpage_rwwee_addr((int)FLASHPAGE_RWWEE_NUMOF - 1), raw_buf, 16);
/* verify that previous write_raw effectively wrote the desired data */
- if (memcmp(flashpage_rwwee_addr((int)FLASHPAGE_RWWEE_NUMOF - 1), raw_buf, strlen(raw_buf)) != 0) {
+ if (memcmp(flashpage_rwwee_addr((int)FLASHPAGE_RWWEE_NUMOF - 1), raw_buf, 16) != 0) {
puts("error verifying the content of last RWWEE page");
return 1;
}
@@ -682,7 +696,7 @@ static const shell_command_t shell_commands[] = {
{ "read", "Copy the given page to the local page buffer and dump to STDOUT", cmd_read },
{ "write", "Write the local page buffer to the given page", cmd_write },
#endif
- { "write_raw", "Write (ASCII, max 64B) data to the given address", cmd_write_raw },
+ { "write_raw", "Write raw bytes (max 64B) to the given address", cmd_write_raw },
{ "erase", "Erase the given page buffer", cmd_erase },
#ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE
{ "edit", "Write bytes to the local page buffer", cmd_edit },
-
BOARD=same54-xpro make -C tests/periph/flashpage flash term
-
in the test do:
erase 124
write_raw 0xf8000 7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
write_raw 0xf8000 3FFFFFFFFF563030FFFFFFFFFFFFFFFF
read 124
-
optionally: merge this and enable debug to make the ECC interrupt visible.
Expected results
The flash memory at 0xf8000
should have been updated with 3FFFFFFFFF563030FFFFFFFFFFFFFFFF
Actual results
The first bit flipped from 0 to 1!
2023-09-17 13:10:38,546 # 00000000 BF FF FF FF FF 56 30 30 FF FF FF FF FF FF FF FF .....V00........
It does not happen on any address or with any data. But it is consistently reproducible with the given example.
GDB
Very weird is that GDB shows the correct data, when it is read:
However, when we read it, it is 0xBF
.
Also edbg
reads it as 0xBF
.
fabian.huessler@ml-pa.loc@MLPA-NB119:~/RIOT/tests/periph/flashpage$ edbg -t same54 -o 0xf8000 -r -f ~/edbg.txt
fabian.huessler@ml-pa.loc@MLPA-NB119:~/RIOT/tests/periph/flashpage$ xxd ~/edbg.txt
00000000: bfff ffff ff56 3030 ffff ffff ffff ffff .....V00........
Steps tried to solve
- disable interrupts while reading/writing
- disable CPU and flashpage cache: errata 2.14 Non-Volatile Memory Controller (NVMCTRL)
- for example overwrite
0x7F
with0xDF
to get0x3F
Versions
master (e153047)