1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/tests/periph/flashpage/main.c
Marian Buschsieweke ee79bb7d68
tests: update tests for MSP430 CPU
Using the builtin `__MSP430__` macro is fool-proof and stable even
if one would try to rename and reorganize the MSP430 cpu code.
2023-06-19 14:49:55 +02:00

722 lines
19 KiB
C

/*
* 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 <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.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 <page>\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 <page>\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 <page>\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 <addr> <data>\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 %u\n",
addr, strlen(raw_buf));
#else
printf("wrote local data to flash address %#" PRIx32 " of len %u\n",
addr, strlen(raw_buf));
#endif
return 0;
}
static int cmd_erase(int argc, char **argv)
{
int page;
if (argc < 2) {
printf("usage: %s <page>\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 <offset> <data>\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 <page>\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 <page>\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 <page>\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 <page>\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;
}