diff --git a/cpu/sam0_common/Makefile.features b/cpu/sam0_common/Makefile.features index 430ceb42af7110d1e98806b6bdb4bb4fcdba4540..cd5a20b58413c87bc25de09e2826be7f0d0ede75 100644 --- a/cpu/sam0_common/Makefile.features +++ b/cpu/sam0_common/Makefile.features @@ -1,4 +1,5 @@ FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_flashpage +FEATURES_PROVIDED += periph_flashpage_raw -include $(RIOTCPU)/cortexm_common/Makefile.features diff --git a/cpu/sam0_common/include/cpu_conf.h b/cpu/sam0_common/include/cpu_conf.h index e954eb139a913c943824a41cc14cbb085130a891..48c1658bcbce68283f43808cbb3ee0d84f6aaa51 100644 --- a/cpu/sam0_common/include/cpu_conf.h +++ b/cpu/sam0_common/include/cpu_conf.h @@ -42,9 +42,15 @@ extern "C" { * @{ */ /* a flashpage in RIOT is mapped to a flash row on the SAM0s */ -#define FLASHPAGE_SIZE (256U) +#define FLASHPAGE_SIZE (256U) /* one SAM0 row contains 4 SAM0 pages -> 4x the amount of RIOT flashpages */ -#define FLASHPAGE_NUMOF (FLASH_NB_OF_PAGES / 4) +#define FLASHPAGE_NUMOF (FLASH_NB_OF_PAGES / 4) +/* The minimum block size which can be written is 16B. However, the erase + * block is always FLASHPAGE_SIZE (SAM0 row). + */ +#define FLASHPAGE_RAW_BLOCKSIZE (16) +/* Writing should be always 4 byte aligned */ +#define FLASHPAGE_RAW_ALIGNMENT (4) /** @} */ #ifdef __cplusplus diff --git a/cpu/sam0_common/periph/flashpage.c b/cpu/sam0_common/periph/flashpage.c index 1090bcd286de179fb99becfa87e4bed4e1e286eb..ee499d6ba84cd616cdc0e6abe9c7cc11484dc4d4 100644 --- a/cpu/sam0_common/periph/flashpage.c +++ b/cpu/sam0_common/periph/flashpage.c @@ -17,7 +17,7 @@ * The sam0 has its flash memory organized in pages and rows, where each row * consists of 4 pages. While pages are writable one at a time, it is only * possible to delete a complete row. This implementation abstracts this - * behavior by only writing complete rows at a time, so the FLASH_PAGE_SIZE we + * behavior by only writing complete rows at a time, so the FLASHPAGE_SIZE we * use in RIOT is actually the row size as specified in the datasheet. * * @author Hauke Petersen <hauke.petersen@fu-berlin.de> @@ -25,18 +25,15 @@ * @} */ +#include <assert.h> + #include "cpu.h" -#include "assert.h" #include "periph/flashpage.h" #define NVMCTRL_PAC_BIT (0x00000002) -void flashpage_write(int page, void *data) +static void _unlock(void) { - assert(page < FLASHPAGE_NUMOF); - - uint32_t *page_addr = (uint32_t *)flashpage_addr(page); - /* remove peripheral access lock for the NVMCTRL peripheral */ #ifdef CPU_FAM_SAML21 PAC->WRCTRL.reg = (PAC_WRCTRL_KEY_CLR | ID_NVMCTRL); @@ -45,19 +42,68 @@ void flashpage_write(int page, void *data) PAC1->WPCLR.reg = NVMCTRL_PAC_BIT; } #endif +} + +static void _lock(void) +{ + /* put peripheral access lock for the NVMCTRL peripheral */ +#ifdef CPU_FAM_SAML21 + PAC->WRCTRL.reg = (PAC_WRCTRL_KEY_SET | ID_NVMCTRL); +#else + if (PAC1->WPCLR.reg & NVMCTRL_PAC_BIT) { + PAC1->WPSET.reg = NVMCTRL_PAC_BIT; + } +#endif +} + +void flashpage_write_raw(void *target_addr, void *data, size_t len) +{ + /* The actual minimal block size for writing is 16B, thus we + * assert we write on multiples and no less of that length. + */ + assert(!(len % FLASHPAGE_RAW_BLOCKSIZE)); + + /* ensure 4 byte aligned writes */ + assert(!(((unsigned)target_addr % FLASHPAGE_RAW_ALIGNMENT) || + ((unsigned)data % FLASHPAGE_RAW_ALIGNMENT))); + + /* ensure the length doesn't exceed the actual flash size */ + assert(((uint8_t*)target_addr + len) < + (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF))); + + uint32_t *dst = (uint32_t *)target_addr; + uint32_t *data_addr = (uint32_t *)data; + + /* write 4 bytes in one go */ + len /= 4; + + _unlock(); + + NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC); + for (unsigned i = 0; i < len; i++) { + *dst++ = *data_addr++; + } + NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP); + + _lock(); +} + +void flashpage_write(int page, void *data) +{ + assert(page < FLASHPAGE_NUMOF); + + uint32_t *page_addr = (uint32_t *)flashpage_addr(page); /* erase given page (the ADDR register uses 16-bit addresses) */ + _unlock(); NVMCTRL->ADDR.reg = (((uint32_t)page_addr) >> 1); NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER); while (!NVMCTRL->INTFLAG.bit.READY) {} + _lock(); /* write data to page */ if (data != NULL) { - uint32_t *data_addr = (uint32_t *)data; - NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC); - for (unsigned i = 0; i < (FLASHPAGE_SIZE / 4); i++) { - *page_addr++ = data_addr[i]; - } - NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP); + flashpage_write_raw(page_addr, data, FLASHPAGE_SIZE); } + } diff --git a/drivers/include/periph/flashpage.h b/drivers/include/periph/flashpage.h index 5fce095333b1b11f3d5a9ff21e4179b1873c4933..c585ddf8c06b407e4591529316a9f3b96d35139e 100644 --- a/drivers/include/periph/flashpage.h +++ b/drivers/include/periph/flashpage.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2016 Freie Universität Berlin + * 2017 Inria * * 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 @@ -28,6 +29,9 @@ * @brief Low-level flash page peripheral driver interface * * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * @author Kaspar Schleiser <kaspar@schleiser.de> + * @author Francisco Acosta <francisco.acosta@inria.fr> + * */ #ifndef PERIPH_FLASHPAGE_H @@ -47,8 +51,30 @@ extern "C" { #ifndef CPU_FLASH_BASE #define CPU_FLASH_BASE (0) #endif +/** + * @def FLASHPAGE_RAW_BLOCKSIZE + * + * @brief For raw writings to the flash, this constant must define the + * minimum write length allowed by the MCU. + */ +#ifdef DOXYGEN +#define FLASHPAGE_RAW_BLOCKSIZE +#endif /** + * @def FLASHPAGE_RAW_ALIGNMENT + * + * @brief The buffers to be written to the flash MUST be aligned, as well as + * the address on which the buffer is written to the flash. This variable + * must be defined for that purpose, according to the MCU align + * requirements. + */ +#ifdef DOXYGEN +#define FLASHPAGE_RAW_ALIGNMENT +#endif +/** + * @def FLASHPAGE_SIZE + * * @brief Make sure the page size and the number of pages is defined */ #ifndef FLASHPAGE_SIZE @@ -101,16 +127,39 @@ static inline int flashpage_page(void *addr) * @brief Write the given page with the given data * * @param[in] page page to write - * @param[in] data data to write to the page, MUST be @p FLASHPAGE_SIZE + * @param[in] data data to write to the page, MUST be FLASHPAGE_SIZE * byte. Set to NULL for page erase only. */ void flashpage_write(int page, void *data); +/** + * @brief Write any number of data bytes to a given location in the + * flash memory + * + * @warning Make sure the targeted memory area is erased before calling + * this function + * + * Both target address and data address must be aligned to + * FLASHPAGE_RAW_ALIGN. @p len must be a multiple of FLASHPAGE_RAW_BLOCKSIZE. + * This function doesn't erase any area in flash, thus be sure the targeted + * memory area is erased before writing on it (using the flashpage_write function). + * + * @param[in] target_addr address in flash to write to. MUST be aligned + * to FLASHPAGE_RAW_ALIGNMENT. + * @param[in] data data to write to the address. MUST be aligned + * to FLASHPAGE_RAW_ALIGNMENT. + * @param[in] len length of the data to be written. It MUST be + * multiple of FLASHPAGE_RAW_BLOCKSIZE. Also, + * ensure it doesn't exceed the actual flash + * memory size. + */ +void flashpage_write_raw(void *target_addr, void *data, size_t len); + /** * @brief Read the given page into the given memory location * * @param[in] page page to read - * @param[out] data memory to write the page to, MUST be @p FLASHPAGE_SIZE + * @param[out] data memory to write the page to, MUST be FLASHPAGE_SIZE * byte */ void flashpage_read(int page, void *data); @@ -119,7 +168,7 @@ void flashpage_read(int page, void *data); * @brief Verify the given page against the given data * * @param[in] page page to verify - * @param[in] data data to compare page against, MUST be @p FLASHPAGE_SIZE + * @param[in] data data to compare page against, MUST be FLASHPAGE_SIZE * byte of data * * @return FLASHPAGE_OK if data in the page is identical to @p data @@ -133,7 +182,7 @@ int flashpage_verify(int page, void *data); * This is a convenience function wrapping flashpage_write and flashpage_verify. * * @param[in] page page to write - * @param[in] data data to write to the page, MUST be @p FLASHPAGE_SIZE + * @param[in] data data to write to the page, MUST be FLASHPAGE_SIZE * byte. * * @return FLASHPAGE_OK on success diff --git a/tests/periph_flashpage/Makefile b/tests/periph_flashpage/Makefile index d9672f7e6f3e6f2a676bc1bcfffef7e273f1498c..b64e980f4d3bb5a56bd88e775bd56a44be7a1331 100644 --- a/tests/periph_flashpage/Makefile +++ b/tests/periph_flashpage/Makefile @@ -1,7 +1,8 @@ BOARD ?= iotlab-m3 include ../Makefile.tests_common -FEATURES_REQUIRED = periph_flashpage +FEATURES_REQUIRED += periph_flashpage +FEATURES_OPTIONAL += periph_flashpage_raw USEMODULE += shell diff --git a/tests/periph_flashpage/main.c b/tests/periph_flashpage/main.c index 41df0f34e996c515c6e22d6125ab8e5a431516c4..e8661a4b006b45483f1819f27be2e84fb4415335 100644 --- a/tests/periph_flashpage/main.c +++ b/tests/periph_flashpage/main.c @@ -32,6 +32,20 @@ */ static uint8_t page_mem[FLASHPAGE_SIZE]; +#ifdef MODULE_PERIPH_FLASHPAGE_RAW +/* + * @brief Allocate an aligned buffer for raw writings + */ +static char raw_buf[64] __attribute__ ((aligned (FLASHPAGE_RAW_ALIGNMENT))); + +static uint32_t getaddr(const char *str) +{ + uint32_t addr = strtol(str, NULL, 16); + + return addr; +} +#endif + static int getpage(const char *str) { int page = atoi(str); @@ -164,6 +178,29 @@ static int cmd_write(int argc, char **argv) return 0; } +#ifdef MODULE_PERIPH_FLASHPAGE_RAW +static int cmd_write_raw(int argc, char **argv) +{ + uint32_t addr; + + if (argc < 3) { + printf("usage: %s <addr> <data>\n", argv[0]); + return 1; + } + + addr = getaddr(argv[1]); + + /* try to align */ + memcpy(raw_buf, argv[2], strlen(argv[2])); + + flashpage_write_raw((void*)addr, raw_buf, strlen(raw_buf)); + + printf("wrote local data to flash address %#lx of len %u\n", + addr, strlen(raw_buf)); + return 0; +} +#endif + static int cmd_erase(int argc, char **argv) { int page; @@ -248,6 +285,9 @@ static const shell_command_t shell_commands[] = { { "dump_local", "Dump the local page buffer to STDOUT", cmd_dump_local }, { "read", "Read and output the given page", cmd_read }, { "write", "Write (ASCII) data to the given page", cmd_write }, +#ifdef MODULE_PERIPH_FLASHPAGE_RAW + { "write_raw", "Write (ASCII, max 64B) data to the given address", cmd_write_raw }, +#endif { "erase", "Erase the given page", cmd_erase }, { "edit", "Write bytes to the local page", cmd_edit }, { "test", "Write and verify test pattern", cmd_test },