diff --git a/boards/native/Makefile.features b/boards/native/Makefile.features index 4ad14738b855..3aeab9864980 100644 --- a/boards/native/Makefile.features +++ b/boards/native/Makefile.features @@ -1,3 +1,5 @@ FEATURES_PROVIDED += transceiver periph_cpuid config cpp FEATURES_PROVIDED += periph_random FEATURES_PROVIDED += periph_rtc +FEATURES_PROVIDED += periph_flash + diff --git a/boards/pca10000/Makefile.features b/boards/pca10000/Makefile.features index 498e6d7eeb20..f4e4c3625fb2 100644 --- a/boards/pca10000/Makefile.features +++ b/boards/pca10000/Makefile.features @@ -1,2 +1,4 @@ FEATURES_PROVIDED += cpp FEATURES_PROVIDED += periph_uart periph_gpio periph_random periph_rtt periph_cpuid +FEATURES_PROVIDED += periph_flash + diff --git a/boards/pca10005/Makefile.features b/boards/pca10005/Makefile.features index 498e6d7eeb20..f4e4c3625fb2 100644 --- a/boards/pca10005/Makefile.features +++ b/boards/pca10005/Makefile.features @@ -1,2 +1,4 @@ FEATURES_PROVIDED += cpp FEATURES_PROVIDED += periph_uart periph_gpio periph_random periph_rtt periph_cpuid +FEATURES_PROVIDED += periph_flash + diff --git a/boards/yunjia-nrf51822/Makefile.features b/boards/yunjia-nrf51822/Makefile.features index 498e6d7eeb20..f4e4c3625fb2 100644 --- a/boards/yunjia-nrf51822/Makefile.features +++ b/boards/yunjia-nrf51822/Makefile.features @@ -1,2 +1,4 @@ FEATURES_PROVIDED += cpp FEATURES_PROVIDED += periph_uart periph_gpio periph_random periph_rtt periph_cpuid +FEATURES_PROVIDED += periph_flash + diff --git a/cpu/native/include/cpu-conf.h b/cpu/native/include/cpu-conf.h index 3cf26d05e66c..fcdf311b8263 100644 --- a/cpu/native/include/cpu-conf.h +++ b/cpu/native/include/cpu-conf.h @@ -68,6 +68,16 @@ extern "C" { #define CPUID_ID_LEN (4) #endif +/** + * @name CPU Flash configuration + * @{ + */ +#define FLASH_PAGE_SIZE (1024) /**< page size of flash memory */ +#define FLASH_NUM_PAGES (256) /**< number of flash pages */ +#define FLASH_WRITE_ALIGN (4) /**< number of bytes which must be written at once */ +#define FLASH_ERASED_WORD_VALUE (0x00) /**< value of erased data words */ +/* @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/native/include/native_internal.h b/cpu/native/include/native_internal.h index b784de8b26ae..1fbdeb0056fc 100644 --- a/cpu/native/include/native_internal.h +++ b/cpu/native/include/native_internal.h @@ -64,6 +64,7 @@ typedef void (*_native_callback_t)(void); void native_cpu_init(void); void native_interrupt_init(void); extern void native_hwtimer_pre_init(void); +void _native_flash_init(void); void native_irq_handler(void); extern void _native_sig_leave_tramp(void); @@ -84,6 +85,8 @@ extern void (*real_free)(void *ptr); extern void* (*real_calloc)(size_t nmemb, size_t size); extern void* (*real_malloc)(size_t size); extern void* (*real_realloc)(void *ptr, size_t size); +extern void* (*real_mmap)(void *addr, size_t length, int prot, int flags, + int fd, off_t offset); extern void (*real_freeaddrinfo)(struct addrinfo *res); extern void (*real_freeifaddrs)(struct ifaddrs *ifa); extern void (*real_srandom)(unsigned int seed); @@ -105,6 +108,7 @@ extern int (*real_getifaddrs)(struct ifaddrs **ifap); extern int (*real_getpid)(void); extern int (*real_ioctl)(int fildes, int request, ...); extern int (*real_listen)(int socket, int backlog); +extern int (*real_munmap)(void *addr, size_t length); extern int (*real_open)(const char *path, int oflag, ...); extern int (*real_pause)(void); extern int (*real_pipe)(int[2]); @@ -119,6 +123,7 @@ extern int (*real_unlink)(const char *); extern long int (*real_random)(void); extern const char* (*real_gai_strerror)(int errcode); extern FILE* (*real_fopen)(const char *path, const char *mode); +extern off_t (*real_lseek)(int fd, off_t offset, int whence); #ifdef __MACH__ #else @@ -147,6 +152,11 @@ extern pid_t _native_pid; extern pid_t _native_id; extern const char *_native_unix_socket_path; +extern const char *_native_flash_path; +extern volatile uint8_t *_native_flash_memory; +extern int _native_flash_fd; +extern size_t _native_flash_size; + #ifdef MODULE_UART0 #include extern fd_set _native_rfds; diff --git a/cpu/native/lpm_cpu.c b/cpu/native/lpm_cpu.c index 3ac45213aa27..fdd43b501d23 100644 --- a/cpu/native/lpm_cpu.c +++ b/cpu/native/lpm_cpu.c @@ -24,6 +24,9 @@ #include #endif #include +#ifdef FEATURE_PERIPH_FLASH +#include +#endif #include "lpm.h" #include "debug.h" @@ -129,6 +132,13 @@ enum lpm_mode lpm_set(enum lpm_mode target) case LPM_OFF: printf("lpm_set(): exit()\n"); +#ifdef FEATURE_PERIPH_FLASH + if (_native_flash_fd != -1) { + /* casting to discard volatile */ + real_munmap((void *)_native_flash_memory, _native_flash_size); + real_close(_native_flash_fd); + } +#endif real_exit(EXIT_SUCCESS); default: diff --git a/cpu/native/native_cpu.c b/cpu/native/native_cpu.c index 5548f0af1579..ba197986fcfc 100644 --- a/cpu/native/native_cpu.c +++ b/cpu/native/native_cpu.c @@ -40,6 +40,7 @@ #endif #include +#include #include "kernel_internal.h" #include "kernel.h" @@ -77,6 +78,13 @@ int reboot_arch(int mode) real_close(_native_tap_fd); } #endif +#ifdef FEATURE_PERIPH_FLASH + if (_native_flash_fd != -1) { + /* casting to discard volatile */ + real_munmap((void *)_native_flash_memory, _native_flash_size); + real_close(_native_flash_fd); + } +#endif if (real_execve(_native_argv[0], _native_argv, NULL) == -1) { err(EXIT_FAILURE, "reboot: execve"); diff --git a/cpu/native/periph/flash.c b/cpu/native/periph/flash.c new file mode 100644 index 000000000000..2bdf85bbf4a7 --- /dev/null +++ b/cpu/native/periph/flash.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2014 Frank Holtz + * Copyright (C) 2014 Ludwig Ortmann + * + * 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. + */ + +/** + * Native CPU periph/flash.h implementation + * + * The implementation emulates flash ROM by allocating RAM. + * When using the "-f" argument, flash contents are synchronized to a + * given file. + * + * @author Frank Holtz + * @author Ludwig Ortmann + * + * @ingroup _native_cpu + * @defgroup _native_flash + * @file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpu-conf.h" +#include "periph/flash.h" + +#include "native_internal.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +int _native_flash_fd = -1; +volatile uint8_t *_native_flash_memory = NULL; +size_t _native_flash_size = (FLASH_NUM_PAGES *FLASH_PAGE_SIZE); + + +/************************************************************************/ +/* internal API *********************************************************/ +/************************************************************************/ + +/** + * @brief Check if given address in valid range + * + * @return Error code FLASH_ERROR_SUCCESS or FLASH_ERROR_ADDR_RANGE + */ +static uint8_t flash_check_address(uint8_t *address) +{ + if ((address < _native_flash_memory) + || ((address >= _native_flash_memory + _native_flash_size))) { + return FLASH_ERROR_ADDR_RANGE; + } + + return FLASH_ERROR_SUCCESS; +} + +/************************************************************************/ +/* periph/flash.h *******************************************************/ +/************************************************************************/ + +uint8_t flash_init(void) +{ + DEBUG("flash initialized\n"); + return FLASH_ERROR_SUCCESS; +} + +flash_page_number_t flash_get_page_number(void *address, flash_page_size_t *page_offset) +{ + flash_page_number_t page_number = (flash_page_number_t)(((uint8_t *)address - + &_native_flash_memory[0]) / FLASH_PAGE_SIZE); + + if (page_offset != NULL) { + *page_offset = (flash_page_number_t)( + ((uint8_t *)address - &_native_flash_memory[0]) + % FLASH_PAGE_SIZE); + } + + return page_number; +} + +void *flash_get_address(flash_page_number_t page) +{ + return ((void *)(_native_flash_memory + (page * FLASH_PAGE_SIZE))); +} + +uint8_t flash_memcpy(void *dest, const void *src, size_t n) +{ + /* Check memory range */ + if (flash_check_address((uint8_t *) dest) > FLASH_ERROR_SUCCESS) { + DEBUG("attempted to write below first address\n"); + return FLASH_ERROR_ADDR_RANGE; + } + + if (flash_check_address((((uint8_t *) dest) + n)) > FLASH_ERROR_SUCCESS) { + DEBUG("attemted to write beyond last address\n"); + return FLASH_ERROR_ADDR_RANGE; + } + + memcpy(dest, src, n); + + return (FLASH_ERROR_SUCCESS); +} + +uint8_t flash_memcpy_fast(void *dest, const void *src, size_t n) +{ + /* Check alignment */ +#if FLASH_WRITE_ALIGN>1 + if ((n % FLASH_WRITE_ALIGN != 0) || + ((size_t)src % FLASH_WRITE_ALIGN != 0) || + ((size_t)dest % FLASH_WRITE_ALIGN != 0)) { + DEBUG("Unaligned access dest=%d, src=%d, n=%d\n", (size_t)mydst, (size_t)mysrc, n); + return FLASH_ERROR_ALIGNMENT; + } + +#endif + + /* Check memory range */ + if (flash_check_address((uint8_t *) dest) > FLASH_ERROR_SUCCESS) { + DEBUG("attempted to write below first address\n"); + return FLASH_ERROR_ADDR_RANGE; + } + + if (flash_check_address((((uint8_t *) dest) + n)) > FLASH_ERROR_SUCCESS) { + DEBUG("attemted to write beyond last address\n"); + return FLASH_ERROR_ADDR_RANGE; + } + + memcpy(dest, src, n); + + return FLASH_ERROR_SUCCESS; +} + +uint8_t flash_erase_page(flash_page_number_t page) +{ + /* check argument */ + if (page > FLASH_NUM_PAGES) { + return FLASH_ERROR_ADDR_RANGE; + } + + /* erase content */ + DEBUG("Erase page %d\n", page); + + for (ssize_t i = page * FLASH_PAGE_SIZE; i < ((page + 1) * FLASH_PAGE_SIZE); i++) { + _native_flash_memory[i] = FLASH_ERASED_WORD_VALUE; + } + + return FLASH_ERROR_SUCCESS; +} + +uint8_t flash_erase_page_by_address(void *address) +{ + if (flash_check_address(address) > FLASH_ERROR_SUCCESS) { + return FLASH_ERROR_ADDR_RANGE; + } + + flash_page_size_t page_offset; + return (flash_erase_page(flash_get_page_number(address, &page_offset))); +} + +/************************************************************************/ +/* native internal API **************************************************/ +/************************************************************************/ + +void _native_flash_init(void) +{ + DEBUG("_native_flash_init\n"); + off_t init_start = 0, init_length = _native_flash_size; + + /* open and mmap file to memory if path given, malloc otherwise */ + if (_native_flash_path != NULL) { + DEBUG("_native_flash_init: opening file\n"); + _native_syscall_enter(); + + /* try to open [existing] file */ + if ((_native_flash_fd = real_open(_native_flash_path, + O_RDWR | O_CREAT, 0600)) == -1) { + err(EXIT_FAILURE, "_native_flash_init: open"); + } + + /* make sure the file is large enough and adjust boundaries + * for initialization */ + size_t size = 0; + char buf[100]; + int ret; + + for (ret = 0; (ret = read(_native_flash_fd, buf, sizeof(buf))) > 0;) { + size += ret; + } + + if (ret == -1) { + err(EXIT_FAILURE, "_native_flash_init: read"); + } + + init_start = size; + + if (size < _native_flash_size) { + init_length = _native_flash_size - size; + + if (real_lseek(_native_flash_fd, _native_flash_size - 1, + SEEK_SET) == -1) { + err(EXIT_FAILURE, "_native_flash_init: lseek"); + } + + if (real_write(_native_flash_fd, "", 1) != 1) { + err(EXIT_FAILURE, "_native_flash_init: write"); + } + } + else { + init_length = 0; + } + + /* try to map file into memory */ + if ((_native_flash_memory = real_mmap(NULL, _native_flash_size, + PROT_READ | PROT_WRITE, MAP_SHARED, _native_flash_fd, 0) + ) == MAP_FAILED) { + err(EXIT_FAILURE, "_native_flash_init: mmap"); + } + + DEBUG("_native_flash_init: using file %s\n", _native_flash_path); + + _native_syscall_leave(); + } + else { + DEBUG("_native_flash_init: allocating memory\n"); + _native_syscall_enter(); + + /* try and allocate memory */ + if ((_native_flash_memory = real_malloc(_native_flash_size)) == NULL) { + err(EXIT_FAILURE, "_native_flash_init: malloc"); + } + + _native_syscall_leave(); + DEBUG("_native_flash_init: using memory %p\n", _native_flash_memory); + } + + /* initialize [remaining] memory area */ + memset((void *)(_native_flash_memory + init_start), FLASH_ERASED_WORD_VALUE, + init_length); +} diff --git a/cpu/native/startup.c b/cpu/native/startup.c index 0d5137a6afcb..b9a0cedaf78b 100644 --- a/cpu/native/startup.c +++ b/cpu/native/startup.c @@ -44,6 +44,7 @@ char **_native_argv; pid_t _native_pid; pid_t _native_id; const char *_native_unix_socket_path = NULL; +const char *_native_flash_path = NULL; /** * initialize _native_null_in_pipe to allow for reading from stdin @@ -200,6 +201,12 @@ void usage_exit(void) -r replay missed output when (re-)attaching to socket\n\ (implies -o)\n"); #endif + +#ifdef FEATURE_PERIPH_FLASH + real_printf("\ +-f Path to a file where simulated flash is stored permanently\n"); +#endif + real_printf("\ -i specify instance id (set by config module)\n\ -d daemonize\n\ @@ -316,6 +323,17 @@ __attribute__((constructor)) static void startup(int argc, char **argv) _native_unix_socket_path = argv[++argp]; } } +#endif +#ifdef FEATURE_PERIPH_FLASH + else if (strcmp("-f", arg) == 0) { + /* parse optional path */ + if ((argp + 1 < argc) && (argv[argp + 1][0] != '-')) { + _native_flash_path = argv[++argp]; + } + else { + usage_exit(); + } + } #endif else { usage_exit(); @@ -340,6 +358,9 @@ __attribute__((constructor)) static void startup(int argc, char **argv) #ifdef MODULE_NATIVENET tap_init(argv[1]); #endif +#ifdef FEATURE_PERIPH_FLASH + _native_flash_init(); +#endif board_init(); diff --git a/cpu/native/syscalls.c b/cpu/native/syscalls.c index f0587ce15055..f524875682cd 100644 --- a/cpu/native/syscalls.c +++ b/cpu/native/syscalls.c @@ -57,6 +57,8 @@ void (*real_free)(void *ptr); void* (*real_malloc)(size_t size); void* (*real_calloc)(size_t nmemb, size_t size); void* (*real_realloc)(void *ptr, size_t size); +void* (*real_mmap)(void *addr, size_t length, int prot, int flags, + int fd, off_t offset); void (*real_freeaddrinfo)(struct addrinfo *res); void (*real_freeifaddrs)(struct ifaddrs *ifa); void (*real_srandom)(unsigned int seed); @@ -75,6 +77,7 @@ int (*real_feof)(FILE *stream); int (*real_ferror)(FILE *stream); int (*real_listen)(int socket, int backlog); int (*real_ioctl)(int fildes, int request, ...); +int (*real_munmap)(void *addr, size_t length); int (*real_open)(const char *path, int oflag, ...); int (*real_pause)(void); int (*real_pipe)(int[2]); @@ -87,6 +90,7 @@ int (*real_unlink)(const char *); long int (*real_random)(void); const char* (*real_gai_strerror)(int errcode); FILE* (*real_fopen)(const char *path, const char *mode); +off_t (*real_lseek)(int fd, off_t offset, int whence); #ifdef __MACH__ #else @@ -405,6 +409,8 @@ void _native_init_syscalls(void) *(void **)(&real_execve) = dlsym(RTLD_NEXT, "execve"); *(void **)(&real_ioctl) = dlsym(RTLD_NEXT, "ioctl"); *(void **)(&real_listen) = dlsym(RTLD_NEXT, "listen"); + *(void **)(&real_mmap) = dlsym(RTLD_NEXT, "mmap"); + *(void **)(&real_munmap) = dlsym(RTLD_NEXT, "munmap"); *(void **)(&real_open) = dlsym(RTLD_NEXT, "open"); *(void **)(&real_pause) = dlsym(RTLD_NEXT, "pause"); *(void **)(&real_fopen) = dlsym(RTLD_NEXT, "fopen"); @@ -412,6 +418,7 @@ void _native_init_syscalls(void) *(void **)(&real_feof) = dlsym(RTLD_NEXT, "feof"); *(void **)(&real_ferror) = dlsym(RTLD_NEXT, "ferror"); *(void **)(&real_clearerr) = dlsym(RTLD_NEXT, "clearerr"); + *(void **)(&real_lseek) = dlsym(RTLD_NEXT, "lseek"); #ifdef __MACH__ #else *(void **)(&real_clock_gettime) = dlsym(RTLD_NEXT, "clock_gettime"); diff --git a/cpu/nrf51822/include/cpu-conf.h b/cpu/nrf51822/include/cpu-conf.h index 51cbd3bbbd62..a11f18da1d5a 100644 --- a/cpu/nrf51822/include/cpu-conf.h +++ b/cpu/nrf51822/include/cpu-conf.h @@ -55,6 +55,17 @@ extern "C" { */ #define CPUID_ID_LEN (8) +/** + * @name CPU Flash configuration + * @{ + */ +#define FLASH_PAGE_SIZE (1024) /**< Page size of flash memory */ +#define FLASH_NUM_PAGES (256) /**< Number of flash pages */ +#define FLASH_WRITE_ALIGN (4) /**< number of bytes must be written at once */ +#define FLASH_ERASED_WORD_VALUE (0xffffffff) /**< value of erased data words */ + +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/nrf51822/periph/flash.c b/cpu/nrf51822/periph/flash.c new file mode 100644 index 000000000000..dba540b379e1 --- /dev/null +++ b/cpu/nrf51822/periph/flash.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2015 Frank Holtz + * Copyright (C) 2015 Ludwig Ortmann + * + * 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. + */ + +/** + * NRF51 periph/flash.h implementation + * + * @author Frank Holtz + * @author Ludwig Ortmann + * + * @ingroup cpu_nrf51822 + * @defgroup cpu_nrf51822_flash + * @file + */ + +#include + +#include "cpu-conf.h" +#include "periph/flash.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* Size of available flash memory */ +static size_t _nrf51_flash_size = (FLASH_NUM_PAGES *FLASH_PAGE_SIZE); + +/************************************************************************/ +/* internal API *********************************************************/ +/************************************************************************/ + +/** + * @brief Check if given address in valid range + * + * @return Error code FLASH_ERROR_SUCCESS or FLASH_ERROR_ADDR_RANGE + * TODO: Implement code region 0|1 check and return FLASH_ERROR_FB_CONFIG + * NRF_FICR->CLENR0 + PPFC + */ +static uint8_t flash_check_address(void *address) +{ + if (((size_t)address < 0) || ((size_t)address >= _nrf51_flash_size)) { + return FLASH_ERROR_ADDR_RANGE; + } + + return FLASH_ERROR_SUCCESS; +} + +/************************************************************************/ +/* periph/flash.h *******************************************************/ +/************************************************************************/ + +uint8_t flash_init(void) +{ + DEBUG("flash initialized\n"); + return FLASH_ERROR_SUCCESS; +} + +flash_page_number_t flash_get_page_number(void *address, flash_page_size_t *page_offset) +{ + flash_page_number_t page_number = (flash_page_number_t)( + (size_t)address / FLASH_PAGE_SIZE); + + if (page_offset != NULL) { + *page_offset = (flash_page_number_t)( + (size_t)address % FLASH_PAGE_SIZE); + } + + return page_number; +} + +void *flash_get_address(flash_page_number_t page) +{ + return ((void *)(page * FLASH_PAGE_SIZE)); +} + +uint8_t flash_memcpy(void *dest, const void *src, size_t n) +{ + DEBUG("flash_memcpy dest=%d, src=%d, n=%d\n", (size_t)dest, (size_t)src, n); + + /* Variables */ + + /* temp */ + uint8_t temp; + + /* Flag is switched on verify error */ + bool writeverify = true; + + /* Buffer for unaligned access*/ + flash_data_word_t dstbuffer; + + /* Align dest */ + flash_data_word_t *mydst_start = + (flash_data_word_t *)((size_t)dest & (FLASH_ERASED_WORD_VALUE - FLASH_WRITE_ALIGN + 1)); + flash_data_word_t *mydst_end = + (flash_data_word_t *)((size_t)((size_t)dest + n) & + (FLASH_ERASED_WORD_VALUE - FLASH_WRITE_ALIGN + 1)); + + /* prepare src to read bytewise */ + uint8_t *mysrc = (uint8_t *)src; + + /* Nothing to do */ + if (n == 0) { + return (FLASH_ERROR_SUCCESS); + } + + /* Check destination memory range: start */ + temp = flash_check_address((uint8_t *) mydst_start); + + if (temp > FLASH_ERROR_SUCCESS) { + DEBUG("attempted to write below first address\n"); + return temp; + } + + /* Check destination memory range: end */ + temp = flash_check_address((uint8_t *) mydst_end - 1); + + if (temp > FLASH_ERROR_SUCCESS) { + DEBUG("attemted to write beyond last address\n"); + return temp; + } + + /* Enable writing to flash */ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos; + + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + + /* move mysrc down to count of unaligned dst bytes */ + mysrc -= (uint8_t *)dest - (uint8_t *)mydst_start; + + for (flash_data_word_t *i = mydst_start; i <= mydst_end && n > 0; i++) { + dstbuffer = FLASH_ERASED_WORD_VALUE; + + /* copy data into dstbuffer */ + for (temp = 0; temp < FLASH_WRITE_ALIGN; temp++) { + /* check address range */ + if ((mysrc >= (uint8_t *)src) && (n > 0)) { + n--; + + /* for every data word byte (little endian)*/ + switch (temp) { + case 0: + *((uint8_t *)&dstbuffer) = *mysrc; + break; + + case 1: + *((uint8_t *)&dstbuffer + 1) = *mysrc; + break; + + case 2: + *((uint8_t *)&dstbuffer + 2) = *mysrc; + break; + + default: + *((uint8_t *)&dstbuffer + 3) = *mysrc; + break; + } + } + + mysrc++; + } + + /* new data -> write + * change & to | when erased bit value is 0 */ + if (*i != (dstbuffer & *i)) { + /* write data */ + *i = dstbuffer; + + /* wait until write operation is completed */ + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + + /* verify data */ + if (*i != dstbuffer) { + /* set error flag */ + writeverify = false; + } + } + } + + /* Disable writing to flash */ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; + + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + + if (writeverify) { + return (FLASH_ERROR_SUCCESS); + } + else { + return (FLASH_ERROR_VERIFY); + } +} + +uint8_t flash_memcpy_fast(void *dest, const void *src, size_t n) +{ + flash_data_word_t *mydst = (flash_data_word_t *)dest; + flash_data_word_t *mysrc = (flash_data_word_t *)src; + + /* Prevent hard fault: Is src and dst aligned ? */ + /* Check alignment */ + if ((n % FLASH_WRITE_ALIGN != 0) || + ((size_t)mysrc % FLASH_WRITE_ALIGN != 0) || + ((size_t)mydst % FLASH_WRITE_ALIGN != 0)) { + DEBUG("Unaligned access dest=%d, src=%d, n=%d\n", (size_t)mydst, (size_t)mysrc, n); + return FLASH_ERROR_ALIGNMENT; + } + + return (0); + + /* Enable writing to flash */ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos; + + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + + /* Write data to flash */ + n = n / FLASH_WRITE_ALIGN; + + while (n--) { + /* write data */ + *mydst = *mysrc; + + /* increment pointer by 4 on Cortex-M0 */ + mydst++; + mysrc++; + + /* wait until write operation is completed */ + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + } + + /* Disable writing to flash */ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; + + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + + return FLASH_ERROR_SUCCESS; +} + +uint8_t flash_erase_page(flash_page_number_t page) +{ + /* check argument */ + if (page > FLASH_NUM_PAGES) { + return FLASH_ERROR_ADDR_RANGE; + } + + /* erase page by address */ + return flash_erase_page_by_address((void *)(page * FLASH_PAGE_SIZE)); +} + +uint8_t flash_erase_page_by_address(void *address) +{ + /* TODO: check code 0|1 region */ + uint8_t ret = flash_check_address(address); + + if (ret > FLASH_ERROR_SUCCESS) { + return ret; + } + + /* Check alignment */ + if ((size_t)address % FLASH_PAGE_SIZE != 0) { + return FLASH_ERROR_ALIGNMENT; + } + + /* Enable erasing flash */ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos; + + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + + + /* Erase page in region 1*/ + NRF_NVMC->ERASEPAGE = (size_t)address; + + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + + /* Disable writing to flash */ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; + + while (NRF_NVMC->READY == NVMC_READY_READY_Busy); + + return FLASH_ERROR_SUCCESS; +} + diff --git a/drivers/include/periph/flash.h b/drivers/include/periph/flash.h new file mode 100644 index 000000000000..070811884190 --- /dev/null +++ b/drivers/include/periph/flash.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2014 Frank Holtz + * + * 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. + */ + +/** + * @defgroup driver_periph_flash Flash interface + * @ingroup driver_periph + * @brief Low-level access to internal flash + * + * @{ + * @file + * @brief Low-level access to internal flash + * + * @author Frank Holtz + */ + +#ifndef __FLASH_H +#define __FLASH_H + +#include +#include "cpu-conf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Available error codes + * + */ +enum flash_errors { + FLASH_ERROR_SUCCESS = 0, /**< Success */ + FLASH_ERROR_BROWNOUT = 1, /**< Voltage to low */ + FLASH_ERROR_FB_CONFIG = 2, /**< Forbidden by MCU configuration/flags */ + FLASH_ERROR_LOCKED = 3, /**< Page or flash is locked by other operation */ + FLASH_ERROR_TIMEOUT = 4, /**< Timeout */ + FLASH_ERROR_ALIGNMENT = 5, /**< Misalligned access */ + FLASH_ERROR_VERIFY = 6, /**< Data not written correctly */ + FLASH_ERROR_ADDR_RANGE = 7, /**< Address out of flash area */ +}; + +/** + * @brief Type definition for page size + * + * This allows page sizes >64k in the future + * + */ +#ifndef FLASH_PAGE_SIZE +#error Flash is not suppored or not implemented for this CPU +#elif FLASH_PAGE_SIZE>=256 +typedef uint16_t flash_page_size_t; +#else +typedef uint8_t flash_page_size_t; +#endif + +/** + * @brief Type definition for page number + * + * This allows more then 64k pages in the future + * + */ +#if FLASH_NUM_PAGES>=256 +typedef uint16_t flash_page_number_t; +#else +typedef uint8_t flash_page_number_t; +#endif + +/** + * @brief Type definition for data word for alignment + */ +#if FLASH_WRITE_ALIGN == 1 +typedef uint8_t flash_data_word_t; +#elif FLASH_WRITE_ALIGN == 2 +typedef uint16_t flash_data_word_t; +#elif FLASH_WRITE_ALIGN == 4 +typedef uint32_t flash_data_word_t; +#elif FLASH_WRITE_ALIGN == 8 +typedef uint64_t flash_data_word_t; +#endif + +/** + * @brief Initialize flash memory access + * + * If needed flash memory initialization can be performed. + * + * @return Error Code + */ +uint8_t flash_init(void); + +/** + * @brief Translates an address to page number + * + * This function is needed to calculate the page number + * for erase operation + * + * @param[in] address Pointer/Memory address + * @param[out] page_offset Address location in page or NULL + * + * @return Flash page number + */ +flash_page_number_t flash_get_page_number(void *address, flash_page_size_t *page_offset); + +/** + * @brief Translates an page number to pointer + * + * This function is needed to calculate the memory address + * of an flash page + * + * @param[in] page Page number + * + * @return Pointer to page or NULL + */ +void *flash_get_address(flash_page_number_t page); + +/** + * @brief Write data to flash + * + * This function works like memcpy. It can be used to write + * a part of a flash page or an single flash page or multiple + * flash pages at once. Written data are reread to check if + * data are stored correctly. Optionally a written word can + * modified to set only changed bits to reach more write cycles. + * + * This function can be used with aligned and unaligned addresses. + * + * The return code should by checked to handle errors. + * + * @param[in] dest Address of flash memory page + * @param[in] src Address of source data + * @param[in] n Number of bytes to write + * + * @return FLASH_ERROR_SUCCESS + * @return FLASH_ERROR_BROWNOUT + * @return FLASH_ERROR_FB_CONFIG + * @return FLASH_ERROR_LOCKED + * @return FLASH_ERROR_TIMEOUT + * @return FLASH_ERROR_VERIFY + * @return FLASH_ERROR_ADDR_RANGE + */ +uint8_t flash_memcpy(void *dest, const void *src, size_t n); + +/** + * @brief Write data to flash without checks + * + * This function works like memcpy. It can be used to write + * a part of a flash page or an single flash page or multiple + * flash pages at once. There are no address or correct write checks, + * so write to protected flash regions are possible. + * + * To support all plattforms do an alignment of src and dest addresses. + * + * This function is used by tests/periph_flash to check if + * alignment check is implemented correctly + * + * The return code should by checked to handle errors. + * + * @param[in] dest Aligned address of flash memory page + * @param[in] src Aligned address of source data + * @param[in] n Aligned number of bytes to write + * + * @return FLASH_ERROR_SUCCESS + * @return FLASH_ERROR_BROWNOUT + * @return FLASH_ERROR_FB_CONFIG + * @return FLASH_ERROR_LOCKED + * @return FLASH_ERROR_TIMEOUT + * @return FLASH_ERROR_ALIGNMENT + */ +uint8_t flash_memcpy_fast(void *dest, const void *src, size_t n); + + +/** + * @brief Erase an flash page by page number + * + * @param[in] page Page number + * + * @return FLASH_ERROR_SUCCESS + * @return FLASH_ERROR_BROWNOUT + * @return FLASH_ERROR_FB_CONFIG + * @return FLASH_ERROR_LOCKED + * @return FLASH_ERROR_TIMEOUT + * @return FLASH_ERROR_ADDR_RANGE + */ +uint8_t flash_erase_page(flash_page_number_t page); + +/** + * @brief Erase an flash page by address + * + * @param[in] address Page address + * + * @return FLASH_ERROR_SUCCESS + * @return FLASH_ERROR_BROWNOUT + * @return FLASH_ERROR_FB_CONFIG + * @return FLASH_ERROR_LOCKED + * @return FLASH_ERROR_TIMEOUT + * @return FLASH_ERROR_ADDR_RANGE + */ +uint8_t flash_erase_page_by_address(void *address); + +#ifdef __cplusplus +} +#endif + +#endif /* __FLASH_H */ +/** @} */ diff --git a/tests/periph_flash/Makefile b/tests/periph_flash/Makefile new file mode 100644 index 000000000000..8b285d792b73 --- /dev/null +++ b/tests/periph_flash/Makefile @@ -0,0 +1,6 @@ +export APPLICATION = periph_flash +include ../Makefile.tests_common + +FEATURES_REQUIRED = periph_flash + +include $(RIOTBASE)/Makefile.include diff --git a/tests/periph_flash/README.md b/tests/periph_flash/README.md new file mode 100644 index 000000000000..a8f93b3161c1 --- /dev/null +++ b/tests/periph_flash/README.md @@ -0,0 +1,4 @@ +Expected result +=============== +This test should be run without errors. With second start "Compare signate with flash OK (0)" must be print out. + diff --git a/tests/periph_flash/main.c b/tests/periph_flash/main.c new file mode 100644 index 000000000000..fc935a74191a --- /dev/null +++ b/tests/periph_flash/main.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2014 Frank Holtz + * + * 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 Flash interface test + * + * @author Frank Holtz + * + * @} + */ + +#include +#include + +#include "periph/flash.h" + +uint8_t print_test(char *text, uint32_t return_code, uint32_t error_code) +{ + if ((return_code == error_code) || (error_code == 65535)) { + printf("%s\tOK (%d)\n", text, (int)return_code); + return 0; + } + else { + printf("%s\tError (%d)\n", text, (int)return_code); + return 1; + } +} + +void dump_line(uint8_t *address) +{ + printf("%08x ", (size_t)address); + + for (uint8_t i = 0; i < 16; i++) { + printf("%02x ", address[i]); + } + + for (uint8_t i = 0; i < 16; i++) { + if ((address[i] > 31) && (address[i] < 127)) { + printf("%c", address[i]); + } + else { + printf("."); + } + } + + printf("\n"); +} + +int main(void) +{ + /* Signature with space for alignment */ + char *testsignature = "RIOTOS-FLASH-TEST\0XXXXXXX"; + char *teststring = "01234"; + int errors = 0, testsignaturelen = strlen(testsignature), counter = 0; + uint8_t *flashpage = flash_get_address(FLASH_NUM_PAGES - 1); + + printf("\nFlash interface test...\n" + "Test\t\t\t\t\tResult (RC)\n" + "---------------------------------------------------\n"); + + errors += print_test("Initializing flash interface\t", flash_init(), FLASH_ERROR_SUCCESS); + errors += print_test("Compare signature with flash\t", memcmp(flashpage + 1, testsignature, + testsignaturelen), 65535); + errors += print_test("Erase last flash page\t\t", flash_erase_page(FLASH_NUM_PAGES - 1), + FLASH_ERROR_SUCCESS); + + for (flash_page_size_t i = 0; i < FLASH_PAGE_SIZE; i++) { + if (flashpage[i] == (FLASH_ERASED_WORD_VALUE & 0xff)) { + counter++; + } + } + + errors += print_test("Number of erased bytes\t\t", counter, FLASH_PAGE_SIZE); + + +#if FLASH_NUM_PAGES != 256 && FLASH_NUM_PAGES != 65536 + errors += print_test("Erase flash page outside\t\t", flash_erase_page(FLASH_NUM_PAGES + 1), + FLASH_ERROR_ADDR_RANGE); +#endif +#if FLASH_WRITE_ALIGN > 1 + errors += print_test("Write test signature unaligned\t", flash_memcpy_fast(flashpage + 1, + testsignature, testsignaturelen), FLASH_ERROR_ALIGNMENT); +#endif + errors += print_test("Write test signature flash_memcpy", flash_memcpy(flashpage + 1, + testsignature, testsignaturelen), FLASH_ERROR_SUCCESS); + errors += print_test("Compare signature with flash\t", memcmp(flashpage + 1, testsignature, + testsignaturelen), 0); + + errors += print_test("Write 1 byte flash_memcpy\t", + flash_memcpy(flashpage + testsignaturelen + 2, + teststring, 1), FLASH_ERROR_SUCCESS); + errors += print_test("Compare 1 byte with flash\t", + memcmp(flashpage + testsignaturelen + 2, teststring, 1), 0); + + errors += print_test("Overwrite with flash_memcpy\t", + flash_memcpy(flashpage + testsignaturelen + 2, + teststring, strlen(teststring)), FLASH_ERROR_SUCCESS); + errors += print_test("Compare bytes with flash\t", + memcmp(flashpage + testsignaturelen + 2, teststring, + strlen(teststring)), 0); + + + printf("\nDone with %d errors.\n\n", errors); + + printf("Dump top of last flash page:\n"); + + for (flash_page_size_t i = 0; i < 16 * 5; i += 16) { + dump_line(flashpage + i); + } + + return 0; +}