From 89c97ae59a2611ee224713cbcb2ef3047903aac0 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 2 Nov 2020 20:19:32 +0100 Subject: [PATCH 1/2] sys/od: allow to specify address offset with od_hex_dump_ext() When dumping memory the printed addresses always start with `00000000`. This can be very confusing and lead to errors. Allow the user to specify a starting address of the printed memory that will be used instead. By introducing a wrapper function, existing users are unaffected. --- sys/include/od.h | 27 ++++++++++++++++++++++++--- sys/od/od.c | 12 ++++++------ tests/od/main.c | 1 + tests/od/tests/01-run.py | 4 ++++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/sys/include/od.h b/sys/include/od.h index 1b0f84d53715..6e58d1ce9d31 100644 --- a/sys/include/od.h +++ b/sys/include/od.h @@ -36,15 +36,36 @@ extern "C" { /** * @brief Dumps memory stored at *data* byte-wise up to *data_len* in * hexadecimal representation to stdout. If the pseudomodlue `od_string` - is used (`USEMODULE += od_string`) the ASCII representation of *data* is - also displayed. + * is used (`USEMODULE += od_string`) the ASCII representation of *data* is + * also displayed. + * The displayed start address of *data* can be given as *offset*. * * @param[in] data Data to dump. * @param[in] data_len Length in bytes of *data* to output. * @param[in] width Number of bytes per line. If *width* is 0, * @ref OD_WIDTH_DEFAULT is assumed as a default value. + * @param[in] offset Adds an offset to the printed memory addresses. + * If the origin of the data is an address in memory, + * this can be used to print the real addresses together + * with the data. */ -void od_hex_dump(const void *data, size_t data_len, uint8_t width); +void od_hex_dump_ext(const void *data, size_t data_len, uint8_t width, uint32_t offset); + +/** + * @brief Dumps memory stored at *data* byte-wise up to *data_len* in + * hexadecimal representation to stdout. If the pseudomodlue `od_string` + * is used (`USEMODULE += od_string`) the ASCII representation of *data* is + * also displayed. + * + * @param[in] data Data to dump. + * @param[in] data_len Length in bytes of *data* to output. + * @param[in] width Number of bytes per line. If *width* is 0, + * @ref OD_WIDTH_DEFAULT is assumed as a default value. + */ +static inline void od_hex_dump(const void *data, size_t data_len, uint8_t width) +{ + od_hex_dump_ext(data, data_len, width, 0); +} #ifdef __cplusplus } diff --git a/sys/od/od.c b/sys/od/od.c index b95080170ade..92dc2be7b76b 100644 --- a/sys/od/od.c +++ b/sys/od/od.c @@ -20,9 +20,9 @@ #include "od.h" -void od_hex_dump(const void *data, size_t data_len, uint8_t width) +void od_hex_dump_ext(const void *data, size_t data_len, uint8_t width, uint32_t offset) { - print_str("00000000"); + print_u32_hex(offset); if (width == 0) { width = OD_WIDTH_DEFAULT; @@ -41,11 +41,11 @@ void od_hex_dump(const void *data, size_t data_len, uint8_t width) print_str(" "); } print_str(" "); - for(unsigned k = 0; k < (i % width) + 1; k++){ - if(isprint(((uint8_t *)data)[str_pos+k])){ + for (unsigned k = 0; k < (i % width) + 1; k++){ + if (isprint(((uint8_t *)data)[str_pos+k])) { putchar(((uint8_t *)data)[str_pos+k]); } - else{ + else { putchar('.'); } } @@ -55,7 +55,7 @@ void od_hex_dump(const void *data, size_t data_len, uint8_t width) putchar('\n'); if (i != (data_len - 1)) { - print_u32_hex((uint32_t)(i + 1)); + print_u32_hex((uint32_t)(offset + i + 1)); } } } diff --git a/tests/od/main.c b/tests/od/main.c index 2d77cfdd9215..2204bf1a9e3f 100644 --- a/tests/od/main.c +++ b/tests/od/main.c @@ -37,6 +37,7 @@ int main(void) CALL(od_hex_dump(long_str, sizeof(long_str), 4)); CALL(od_hex_dump(long_str, sizeof(long_str), 3)); CALL(od_hex_dump(long_str, sizeof(long_str), 8)); + CALL(od_hex_dump_ext(long_str, sizeof(long_str), 8, 0x100)); return 0; } diff --git a/tests/od/tests/01-run.py b/tests/od/tests/01-run.py index a9de23120b1f..0334fe6d9caf 100755 --- a/tests/od/tests/01-run.py +++ b/tests/od/tests/01-run.py @@ -35,6 +35,10 @@ def testfunc(child): child.expect_exact("00000000 FF 2C 61 FF 2E 62 63 64 .,a..bcd") child.expect_exact("00000008 65 66 67 68 69 6A 6B 6C efghijkl") child.expect_exact("00000010 6D 6E 6F 70 00 mnop.") + child.expect_exact("od_hex_dump_ext(long_str, sizeof(long_str), 8, 0x100)") + child.expect_exact("00000100 FF 2C 61 FF 2E 62 63 64 .,a..bcd") + child.expect_exact("00000108 65 66 67 68 69 6A 6B 6C efghijkl") + child.expect_exact("00000110 6D 6E 6F 70 00 mnop.") print("All tests successful") From cf2ac213fc0ead8d39bed50506e393143e4881fa Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sun, 1 Nov 2020 17:52:46 +0100 Subject: [PATCH 2/2] tests/mtd_raw: add simple test for MTD --- tests/mtd_raw/Makefile | 9 + tests/mtd_raw/Makefile.ci | 7 + tests/mtd_raw/main.c | 456 ++++++++++++++++++++++++++++++++++ tests/mtd_raw/tests/01-run.py | 24 ++ 4 files changed, 496 insertions(+) create mode 100644 tests/mtd_raw/Makefile create mode 100644 tests/mtd_raw/Makefile.ci create mode 100644 tests/mtd_raw/main.c create mode 100755 tests/mtd_raw/tests/01-run.py diff --git a/tests/mtd_raw/Makefile b/tests/mtd_raw/Makefile new file mode 100644 index 000000000000..bea9c1e940fd --- /dev/null +++ b/tests/mtd_raw/Makefile @@ -0,0 +1,9 @@ +include ../Makefile.tests_common + +USEMODULE += shell +USEMODULE += shell_commands + +USEMODULE += od +USEMODULE += mtd + +include $(RIOTBASE)/Makefile.include diff --git a/tests/mtd_raw/Makefile.ci b/tests/mtd_raw/Makefile.ci new file mode 100644 index 000000000000..75912a97e3c7 --- /dev/null +++ b/tests/mtd_raw/Makefile.ci @@ -0,0 +1,7 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p \ + # diff --git a/tests/mtd_raw/main.c b/tests/mtd_raw/main.c new file mode 100644 index 000000000000..2582f53cdfe8 --- /dev/null +++ b/tests/mtd_raw/main.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2020 ML!PA Consulting GmbH + * + * 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 Application for testing MTD implementations + * + * @author Benjamin Valentin + * + * @} + */ + +#include +#include +#include +#include +#include +#include + +#include "od.h" +#include "mtd.h" +#include "shell.h" +#include "board.h" +#include "macros/units.h" + +#ifndef MTD_NUMOF +#ifdef MTD_0 +#define MTD_NUMOF 1 +#else +#define MTD_NUMOF 0 +#endif +#endif + +static mtd_dev_t *_get_mtd_dev(unsigned idx) +{ + switch (idx) { +#ifdef MTD_0 + case 0: return MTD_0; +#endif +#ifdef MTD_1 + case 1: return MTD_1; +#endif +#ifdef MTD_2 + case 2: return MTD_2; +#endif +#ifdef MTD_3 + case 3: return MTD_3; +#endif + } + + return NULL; +} + +static mtd_dev_t *_get_dev(int argc, char **argv) +{ + if (argc < 2) { + printf("%s: please specify the MTD device\n", argv[0]); + return NULL; + } + + unsigned idx = atoi(argv[1]); + + if (idx > MTD_NUMOF) { + printf("%s: invalid device: %s\n", argv[0], argv[1]); + return NULL; + } + + return _get_mtd_dev(idx); +} + +static uint64_t _get_size(mtd_dev_t *dev) +{ + return (uint64_t)dev->sector_count + * dev->pages_per_sector + * dev->page_size; +} + +static int cmd_read(int argc, char **argv) +{ + mtd_dev_t *dev = _get_dev(argc, argv); + uint32_t addr, len; + + if (argc < 4 || dev == NULL) { + printf("usage: %s \n", argv[0]); + return -1; + } + + addr = atoi(argv[2]); + len = atoi(argv[3]); + + void *buffer = malloc(len); + if (buffer == NULL) { + puts("out of memory"); + return -1; + } + + /* don't print random data if read fails */ + memset(buffer, 0x3F, len); + + int res = mtd_read(dev, buffer, addr, len); + + od_hex_dump_ext(buffer, len, 0, addr); + + free(buffer); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static int cmd_read_page(int argc, char **argv) +{ + mtd_dev_t *dev = _get_dev(argc, argv); + uint32_t page, offset, len; + + if (argc < 5 || dev == NULL) { + printf("usage: %s \n", argv[0]); + return -1; + } + + page = atoi(argv[2]); + offset = atoi(argv[3]); + len = atoi(argv[4]); + + void *buffer = malloc(len); + if (buffer == NULL) { + puts("out of memory"); + return -1; + } + + int res = mtd_read_page(dev, buffer, page, offset, len); + + od_hex_dump_ext(buffer, len, 0, page * dev->page_size + offset); + + free(buffer); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static int cmd_write(int argc, char **argv) +{ + mtd_dev_t *dev = _get_dev(argc, argv); + uint32_t addr, len; + + if (argc < 4 || dev == NULL) { + printf("usage: %s \n", argv[0]); + return -1; + } + + addr = atoi(argv[2]); + len = strlen(argv[3]); + + int res = mtd_write(dev, argv[3], addr, len); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static int cmd_write_page(int argc, char **argv) +{ + mtd_dev_t *dev = _get_dev(argc, argv); + uint32_t page, offset, len; + + if (argc < 5 || dev == NULL) { + printf("usage: %s \n", argv[0]); + return -1; + } + + page = atoi(argv[2]); + offset = atoi(argv[3]); + len = strlen(argv[4]); + + int res = mtd_write_page(dev, argv[4], page, offset, len); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static int cmd_erase(int argc, char **argv) +{ + mtd_dev_t *dev = _get_dev(argc, argv); + uint32_t addr; + uint32_t len; + + if (argc < 4 || dev == NULL) { + printf("usage: %s \n", argv[0]); + return -1; + } + + addr = atoi(argv[2]); + len = atoi(argv[3]); + + int res = mtd_erase(dev, addr, len); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static int cmd_erase_sector(int argc, char **argv) +{ + mtd_dev_t *dev = _get_dev(argc, argv); + uint32_t sector, count = 1; + + if (argc < 3 || dev == NULL) { + printf("usage: %s [count]\n", argv[0]); + return -1; + } + + sector = atoi(argv[2]); + + if (argc > 3) { + count = atoi(argv[3]); + } + + int res = mtd_erase_sector(dev, sector, count); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static void _print_size(uint64_t size) +{ + unsigned long len; + const char *unit; + + if (size == 0) { + len = 0; + unit = "byte"; + } else if ((size & (GiB(1) - 1)) == 0) { + len = size / GiB(1); + unit = "GiB"; + } + else if ((size & (MiB(1) - 1)) == 0) { + len = size / MiB(1); + unit = "MiB"; + } + else if ((size & (KiB(1) - 1)) == 0) { + len = size / KiB(1); + unit = "kiB"; + } else { + len = size; + unit = "byte"; + } + + printf("total: %lu %s\n", len, unit); +} + +static void _print_info(mtd_dev_t *dev) +{ + printf("sectors: %"PRIu32"\n", dev->sector_count); + printf("pages per sector: %"PRIu32"\n", dev->pages_per_sector); + printf("page size: %"PRIu32"\n", dev->page_size); + _print_size(_get_size(dev)); +} + +static int cmd_info(int argc, char **argv) +{ + if (argc < 2) { + printf("mtd devices: %d\n", MTD_NUMOF); + + for (int i = 0; i < MTD_NUMOF; ++i) { + printf(" -=[ MTD_%d ]=-\n", i); + _print_info(_get_mtd_dev(i)); + } + return 0; + } + + mtd_dev_t *dev = _get_dev(argc, argv); + + if (dev == NULL) { + return -1; + } + + _print_info(dev); + + return 0; +} + +static inline int _print_power_usage(const char *progname) +{ + printf("usage: %s \n", progname); + return -1; +} + +static int cmd_power(int argc, char **argv) +{ + mtd_dev_t *dev = _get_dev(argc, argv); + enum mtd_power_state state; + + if (argc < 3 || dev == NULL) { + return _print_power_usage(argv[0]); + } + + if (strcmp(argv[2], "off") == 0) { + state = MTD_POWER_DOWN; + } else if (strcmp(argv[2], "on") == 0) { + state = MTD_POWER_UP; + } else { + return _print_power_usage(argv[0]); + } + + mtd_power(dev, state); + + return 0; +} + +static bool mem_is_all_set(const uint8_t *buf, uint8_t c, size_t n) +{ + for (const uint8_t *end = buf + n; buf != end; ++buf) { + if (*buf != c) { + return false; + } + } + + return true; +} + +static int cmd_test(int argc, char **argv) +{ + mtd_dev_t *dev = _get_dev(argc, argv); + uint32_t sector; + + if (argc < 2 || dev == NULL) { + printf("usage: %s \n", argv[0]); + return -1; + } + + if (dev->sector_count < 2) { + return -1; + } + + if (argc > 2) { + sector = atoi(argv[2]); + } else { + sector = dev->sector_count - 2; + } + + uint8_t *buffer = malloc(dev->page_size); + + if (buffer == NULL) { + puts("out of memory"); + return -1; + } + + uint32_t page_0 = dev->pages_per_sector * sector; + uint32_t page_1 = dev->pages_per_sector * (sector + 1); + uint32_t page_size = dev->page_size; + + puts("[START]"); + + /* write dummy data to sectors */ + memset(buffer, 0x23, dev->page_size); + assert(mtd_write_page(dev, buffer, page_0, 0, page_size) == 0); + assert(mtd_write_page(dev, buffer, page_1, 0, page_size) == 0); + + /* erase two sectors and check if they have been erase */ + assert(mtd_erase_sector(dev, sector, 2) == 0); + assert(mtd_read_page(dev, buffer, page_0, 0, page_size) == 0); + assert(mem_is_all_set(buffer, 0xFF, page_size)); + assert(mtd_read_page(dev, buffer, page_1, 0, page_size) == 0); + assert(mem_is_all_set(buffer, 0xFF, page_size)); + + /* write test data & read it back */ + const char test_str[] = "0123456789"; + uint32_t offset = 5; + assert(mtd_write_page(dev, test_str, page_0, offset, sizeof(test_str)) == 0); + assert(mtd_read_page(dev, buffer, page_0, offset, sizeof(test_str)) == 0); + assert(memcmp(test_str, buffer, sizeof(test_str)) == 0); + + /* write across page boundary */ + offset = page_size - sizeof(test_str) / 2; + assert(mtd_write_page(dev, test_str, page_0, offset, sizeof(test_str)) == 0); + assert(mtd_read_page(dev, buffer, page_0, offset, sizeof(test_str)) == 0); + assert(memcmp(test_str, buffer, sizeof(test_str)) == 0); + + /* write across sector boundary */ + offset = page_size - sizeof(test_str) / 2 + + (dev->pages_per_sector - 1) * page_size; + assert(mtd_write_page(dev, test_str, page_0, offset, sizeof(test_str)) == 0); + assert(mtd_read_page(dev, buffer, page_0, offset, sizeof(test_str)) == 0); + assert(memcmp(test_str, buffer, sizeof(test_str)) == 0); + + puts("[SUCCESS]"); + + free(buffer); + + return 0; +} + +static const shell_command_t shell_commands[] = { + { "info", "Print properties of the MTD device", cmd_info }, + { "power", "Turn the MTD device on/off", cmd_power }, + { "read", "Read a region of memory on the MTD device", cmd_read }, + { "read_page", "Read a region of memory on the MTD device (pagewise addressing)", cmd_read_page }, + { "write", "Write a region of memory on the MTD device", cmd_write }, + { "write_page", "Write a region of memory on the MTD device (pagewise addressing)", cmd_write_page }, + { "erase", "Erase a region of memory on the MTD device", cmd_erase }, + { "erase_sector", "Erase a sector of memory on the MTD device", cmd_erase_sector }, + { "test", "Erase & write test data to the last two sectors", cmd_test }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("Manual MTD test"); + + if (MTD_NUMOF == 0) { + puts("no MTD device present on the board."); + } + + for (int i = 0; i < MTD_NUMOF; ++i) { + printf("init MTD_%d… ", i); + + mtd_dev_t *dev = _get_mtd_dev(i); + int res = mtd_init(dev); + if (res) { + printf("error: %d\n", res); + continue; + } + + printf("OK (%lu kiB)\n", (unsigned long)(_get_size(dev) / 1024)); + mtd_power(dev, MTD_POWER_UP); + } + + /* run the shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/tests/mtd_raw/tests/01-run.py b/tests/mtd_raw/tests/01-run.py new file mode 100755 index 000000000000..ee1ce5e3cefd --- /dev/null +++ b/tests/mtd_raw/tests/01-run.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2020 Benjamin Valentin +# +# 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. + +import sys +from testrunner import run + + +def testfunc(child): + child.sendline("info") + child.expect(r'mtd devices: (\d+)') + mtd_numof = int(child.match.group(1)) + for dev in range(mtd_numof): + child.sendline("test " + str(dev)) + child.expect_exact("[START]") + child.expect_exact("[SUCCESS]") + + +if __name__ == "__main__": + sys.exit(run(testfunc))