Skip to content

cpu/sam0_common/periph/flashpage.c: bit error on read, causing data corruption on same54-xpro. #19928

@fabian18

Description

@fabian18

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:

Screenshot from 2023-09-17 13-20-53

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

Versions

master (e153047)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions