/* * Copyright (C) 2015 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup tests * @{ * * @file * @brief Manual test application for flashpage peripheral drivers * * @author Hauke Petersen * * @} */ #include #include #include #include #include #include "architecture.h" #include "od.h" #include "shell.h" #include "periph/flashpage.h" #include "unaligned.h" #define LINE_LEN (16) /* When writing raw bytes on flash, data must be correctly aligned. */ #define ALIGNMENT_ATTR __attribute__((aligned(FLASHPAGE_WRITE_BLOCK_ALIGNMENT))) /* We must not write chunks smaller than FLASHPAGE_WRITE_BLOCK_SIZE */ #if FLASHPAGE_WRITE_BLOCK_SIZE > 64 #define RAW_BUF_SIZE FLASHPAGE_WRITE_BLOCK_SIZE #else #define RAW_BUF_SIZE 64 #endif /* * @brief Allocate an aligned buffer for raw writings */ static char raw_buf[RAW_BUF_SIZE] ALIGNMENT_ATTR; #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE /** * @brief Allocate space for 1 flash page in RAM * * @note The flash page in RAM must be correctly aligned, even in RAM, when * using flashpage. This is because some architecture uses * 32 bit alignment implicitly and there are cases (stm32l4) that * requires 64 bit alignment. */ static uint8_t page_mem[FLASHPAGE_SIZE] ALIGNMENT_ATTR; #ifdef MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE /** * @brief Reserve 1 page of flash memory */ FLASH_WRITABLE_INIT(_backing_memory, 0x1); /* * @brief Created to test the sorting of symbols in .flash_writable section */ FLASH_WRITABLE_INIT(_abacking_memory, 0x1); #endif /* MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE */ #endif /* MODULE_PERIPH_FLASHPAGE_PAGEWISE */ static int getpage(const char *str) { int page = atoi(str); if ((page >= (int)FLASHPAGE_NUMOF) || (page < 0)) { printf("error: page %i is invalid\n", page); return -1; } return page; } #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE static void memdump(void *addr, size_t len) { od_hex_dump (addr, len, LINE_LEN); } static void dump_local(void) { puts("Local page buffer:"); memdump(page_mem, FLASHPAGE_SIZE); } #endif static int cmd_info(int argc, char **argv) { (void)argc; (void)argv; printf("Flash start addr:\t\t0x%08x\n", (int)CPU_FLASH_BASE); #ifdef FLASHPAGE_SIZE printf("Page size:\t\t\t%i\n", (int)FLASHPAGE_SIZE); #else puts("Page size:\t\t\tvariable"); #endif printf("Number of pages:\t\t%i\n", (int)FLASHPAGE_NUMOF); #ifdef FLASHPAGE_RWWEE_NUMOF printf("RWWEE Flash start addr:\t\t0x%08x\n", (int)CPU_FLASH_RWWEE_BASE); printf("RWWEE Number of pages:\t\t%i\n", (int)FLASHPAGE_RWWEE_NUMOF); #endif #ifdef NVMCTRL_USER printf("AUX page size:\t\t%i\n", FLASH_USER_PAGE_AUX_SIZE + sizeof(nvm_user_page_t)); printf(" user area:\t\t%i\n", FLASH_USER_PAGE_AUX_SIZE); #endif printf("Number of first free page: \t%u \n", flashpage_first_free()); printf("Number of last free page: \t%u \n", flashpage_last_free()); return 0; } #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE static int cmd_dump(int argc, char **argv) { int page; void *addr; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } page = getpage(argv[1]); if (page < 0) { return 1; } addr = flashpage_addr(page); printf("Flash page %i at address %p\n", page, addr); memdump(addr, flashpage_size(page)); return 0; } static int cmd_dump_local(int argc, char **argv) { (void)argc; (void)argv; dump_local(); return 0; } static int cmd_read(int argc, char **argv) { int page; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } page = getpage(argv[1]); if (page < 0) { return 1; } flashpage_read(page, page_mem); printf("Read flash page %i into local page buffer\n", page); dump_local(); return 0; } #endif #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE static int cmd_write(int argc, char **argv) { int page; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } page = getpage(argv[1]); if (page < 0) { return 1; } if (flashpage_write_and_verify(page, page_mem) != FLASHPAGE_OK) { printf("error: verification for page %i failed\n", page); return 1; } printf("wrote local page buffer to flash page %i at addr %p\n", page, flashpage_addr(page)); return 0; } #endif static uint32_t getaddr(const char *str) { uint32_t addr = strtol(str, NULL, 16); return addr; } static int cmd_write_raw(int argc, char **argv) { #if (__SIZEOF_POINTER__ == 2) uint16_t addr; #else uint32_t addr; #endif if (argc < 3) { printf("usage: %s \n", argv[0]); return 1; } #if (__SIZEOF_POINTER__ == 2) addr = (uint16_t) getaddr(argv[1]); #else addr = getaddr(argv[1]); #endif /* try to align */ memcpy(raw_buf, argv[2], strlen(argv[2])); flashpage_write((void*)addr, raw_buf, strlen(raw_buf)); #if (__SIZEOF_POINTER__ == 2) printf("wrote local data to flash address %#" PRIx16 " of len %" PRIuSIZE "\n", addr, strlen(raw_buf)); #else printf("wrote local data to flash address %#" PRIx32 " of len %" PRIuSIZE "\n", addr, strlen(raw_buf)); #endif return 0; } static int cmd_erase(int argc, char **argv) { int page; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } page = getpage(argv[1]); if (page < 0) { return 1; } flashpage_erase(page); printf("successfully erased page %i (addr %p)\n", page, flashpage_addr(page)); return 0; } #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE static int cmd_edit(int argc, char **argv) { int offset; size_t data_len; if (argc < 3) { printf("usage: %s \n", argv[0]); return 1; } offset = atoi(argv[1]); if (offset >= (int)FLASHPAGE_SIZE) { printf("error: given offset is out of bounce\n"); return -1; } data_len = strlen(argv[2]); if ((data_len + offset) > FLASHPAGE_SIZE) { data_len = FLASHPAGE_SIZE - offset; } memcpy(&page_mem[offset], argv[2], data_len); dump_local(); return 0; } static int cmd_test(int argc, char **argv) { int page; char fill = 'a'; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } page = getpage(argv[1]); if (page < 0) { return 1; } for (unsigned i = 0; i < sizeof(page_mem); i++) { page_mem[i] = (uint8_t)fill++; if (fill > 'z') { fill = 'a'; } } if (flashpage_write_and_verify(page, page_mem) != FLASHPAGE_OK) { printf("error verifying the content of page %i\n", page); return 1; } printf("wrote local page buffer to flash page %i at addr %p\n", page, flashpage_addr(page)); return 0; } /** * @brief Does a write and verify test on last page available * * @note Since every hardware can have different flash layouts for * automated testing we always write to the last page available * so we are independent of the size or layout */ static int cmd_test_last(int argc, char **argv) { (void) argc; (void) argv; char fill = 'a'; unsigned last_free = flashpage_last_free(); for (unsigned i = 0; i < sizeof(page_mem); i++) { page_mem[i] = (uint8_t)fill++; if (fill > 'z') { fill = 'a'; } } #ifdef __MSP430__ printf("The last page holds the ISR vector, so test page %u\n", last_free); #endif if (flashpage_write_and_verify(last_free, page_mem) != FLASHPAGE_OK) { puts("error verifying the content of last page"); return 1; } puts("wrote local page buffer to last flash page"); return 0; } #ifdef MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE /** * @brief Does a write and verify test on reserved page */ static int cmd_test_reserved(int argc, char **argv) { (void) argc; (void) argv; /** * Arrays created by the FLASH_WRITABLE_INIT macro should be sorted in * ascending order by name. */ assert(&_abacking_memory < &_backing_memory); char fill = 'a'; const char sig[] = {"RIOT"}; unsigned page = flashpage_page((void *)_backing_memory); printf("Reserved page num: %u \n", page); flashpage_read(page, page_mem); /* test is running for the first time so initialize flash */ if (memcmp(sig, &page_mem[1], sizeof(sig)) != 0) { page_mem[0] = 0; memcpy(&page_mem[1], sig, sizeof(sig)); } else { page_mem[0]++; } printf("Since the last firmware update this test has been run " "%u times \n", page_mem[0]); /* fill memory after counter and signature */ for (unsigned i = 0x1 + sizeof(sig); i < sizeof(page_mem); i++) { page_mem[i] = (uint8_t)fill++; if (fill > 'z') { fill = 'a'; } } if (flashpage_write_and_verify(page, page_mem) != FLASHPAGE_OK) { puts("error verifying the content of reserved page"); return 1; } puts("wrote local page buffer to reserved flash page"); puts("\nWhen running on a bootloader, as an extra check, try restarting " "the board and check whether this application still comes up."); return 0; } #endif /* MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE */ #endif /* MODULE_PERIPH_FLASHPAGE_PAGEWISE */ /** * @brief Does a short raw write on last page available * * @note Since every hardware can have different flash layouts for * automated testing we always write to the last page available * so we are independent of the size or layout */ static int cmd_test_last_raw(int argc, char **argv) { (void) argc; (void) argv; unsigned last_free = flashpage_last_free(); memset(raw_buf, 0, sizeof(raw_buf)); /* try to align */ memcpy(raw_buf, "test12344321tset", 16); #ifdef __MSP430__ printf("The last page holds the ISR vector, so test page %u\n", last_free); #endif /* erase the page first */ flashpage_erase(last_free); 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) { puts("error verifying the content of last page"); return 1; } puts("wrote raw short buffer to last flash page"); return 0; } #ifdef FLASHPAGE_RWWEE_NUMOF static int getpage_rwwee(const char *str) { int page = atoi(str); if ((page >= (int)FLASHPAGE_RWWEE_NUMOF) || (page < 0)) { printf("error: RWWEE page %i is invalid\n", page); return -1; } return page; } static int cmd_read_rwwee(int argc, char **argv) { int page; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } page = getpage_rwwee(argv[1]); if (page < 0) { return 1; } flashpage_rwwee_read(page, page_mem); printf("Read RWWEE flash page %i into local page buffer\n", page); dump_local(); return 0; } static int cmd_write_rwwee(int argc, char **argv) { int page; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } page = getpage_rwwee(argv[1]); if (page < 0) { return 1; } if (flashpage_rwwee_write_and_verify(page, page_mem) != FLASHPAGE_OK) { printf("error: verification for RWWEE page %i failed\n", page); return 1; } printf("wrote local page buffer to RWWEE flash page %i at addr %p\n", page, flashpage_rwwee_addr(page)); return 0; } static int cmd_test_rwwee(int argc, char **argv) { int page; char fill = 'a'; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } page = getpage_rwwee(argv[1]); if (page < 0) { return 1; } fill += (page % ('z' - 'a')); // Make each page slightly different by changing starting char for easier comparison by eye for (unsigned i = 0; i < sizeof(page_mem); i++) { page_mem[i] = (uint8_t)fill++; if (fill > 'z') { fill = 'a'; } } if (flashpage_rwwee_write_and_verify(page, page_mem) != FLASHPAGE_OK) { printf("error verifying the content of RWWEE page %i\n", page); return 1; } printf("wrote local page buffer to RWWEE flash page %i at addr %p\n", page, flashpage_rwwee_addr(page)); return 0; } /** * @brief Does a write and verify test on last page available * * @note Since every hardware can have different flash layouts for * automated testing we always write to the last page available * so we are independent of the size or layout */ static int cmd_test_last_rwwee(int argc, char **argv) { (void) argc; (void) argv; char fill = 'a'; for (unsigned i = 0; i < sizeof(page_mem); i++) { page_mem[i] = (uint8_t)fill++; if (fill > 'z') { fill = 'a'; } } if (flashpage_rwwee_write_and_verify((int)FLASHPAGE_RWWEE_NUMOF - 1, page_mem) != FLASHPAGE_OK) { puts("error verifying the content of last RWWEE page"); return 1; } puts("wrote local page buffer to last RWWEE flash page"); return 0; } /** * @brief Does a short raw write on last page available * * @note Since every hardware can have different flash layouts for * automated testing we always write to the last page available * so we are independent of the size or layout */ static int cmd_test_last_rwwee_raw(int argc, char **argv) { (void) argc; (void) argv; /* try to align */ memcpy(raw_buf, "test12344321tset", 16); /* 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)); /* 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) { puts("error verifying the content of last RWWEE page"); return 1; } puts("wrote raw short buffer to last RWWEE flash page"); return 0; } #endif #ifdef NVMCTRL_USER static int cmd_dump_config(int argc, char **argv) { (void) argc; (void) argv; #ifdef FLASH_USER_PAGE_SIZE od_hex_dump_ext((void*)NVMCTRL_USER, FLASH_USER_PAGE_SIZE, 0, NVMCTRL_USER); #else od_hex_dump_ext((void*)NVMCTRL_USER, AUX_PAGE_SIZE * AUX_NB_OF_PAGES, 0, NVMCTRL_USER); #endif return 0; } 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 }; assert(((int32_t)FLASH_USER_PAGE_AUX_SIZE - (int32_t)(sizeof(test_data) + 2 + 3)) > 0); puts("[START]"); 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; /* 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; } /* 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]"); return 0; } #endif /* NVMCTRL_USER */ static const shell_command_t shell_commands[] = { { "info", "Show information about pages", cmd_info }, #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE { "dump", "Dump the selected page to STDOUT", cmd_dump }, { "dump_local", "Dump the local page buffer to STDOUT", cmd_dump_local }, { "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 }, { "erase", "Erase the given page buffer", cmd_erase }, #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE { "edit", "Write bytes to the local page buffer", cmd_edit }, { "test", "Write and verify test pattern", cmd_test }, { "test_last_pagewise", "Write and verify test pattern on last page available", cmd_test_last }, #endif #ifdef MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE { "test_reserved_pagewise", "Write and verify short write on reserved page", cmd_test_reserved}, #endif { "test_last_raw", "Write and verify raw short write on last page available", cmd_test_last_raw }, #ifdef FLASHPAGE_RWWEE_NUMOF { "read_rwwee", "Copy the given page from RWWEE to the local page buffer and dump to STDOUT", cmd_read_rwwee }, { "write_rwwee", "Write the local page buffer to the given RWWEE page", cmd_write_rwwee }, { "test_rwwee", "Write and verify test pattern to RWWEE", cmd_test_rwwee }, { "test_last_rwwee", "Write and verify test pattern on last RWWEE page available", cmd_test_last_rwwee }, { "test_last_rwwee_raw", "Write and verify raw short write on last RWWEE page available", cmd_test_last_rwwee_raw }, #endif #ifdef NVMCTRL_USER { "dump_config_page", "Dump the content of the MCU configuration page", cmd_dump_config }, { "test_config_page", "Test writing config page. (!DANGER ZONE!)", cmd_test_config }, #endif { NULL, NULL, NULL } }; int main(void) { puts("ROM flash read write test\n"); puts("Please refer to the README.md for further information\n"); cmd_info(0, NULL); /* run the shell */ char line_buf[SHELL_DEFAULT_BUFSIZE]; shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); return 0; }