From 5faafac09295104c3c88a5de6ad908e602898d4b Mon Sep 17 00:00:00 2001 From: Federico Pellegrin Date: Sun, 27 Jan 2019 11:29:35 +0100 Subject: [PATCH] sam0 flashpage RWWEE flash support --- cpu/sam0_common/Makefile.features | 1 + cpu/sam0_common/include/cpu_conf.h | 16 +++ cpu/sam0_common/periph/flashpage.c | 111 +++++++++++++++++-- drivers/include/periph/flashpage.h | 118 ++++++++++++++++++++ drivers/periph_common/flashpage.c | 30 +++++ tests/periph_flashpage/Makefile | 1 + tests/periph_flashpage/main.c | 171 ++++++++++++++++++++++++++++- 7 files changed, 437 insertions(+), 11 deletions(-) diff --git a/cpu/sam0_common/Makefile.features b/cpu/sam0_common/Makefile.features index 534510c773..a8147a397f 100644 --- a/cpu/sam0_common/Makefile.features +++ b/cpu/sam0_common/Makefile.features @@ -1,6 +1,7 @@ FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_flashpage FEATURES_PROVIDED += periph_flashpage_raw +FEATURES_PROVIDED += periph_flashpage_rwee FEATURES_PROVIDED += periph_gpio periph_gpio_irq -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 32168a7e19..563eccda45 100644 --- a/cpu/sam0_common/include/cpu_conf.h +++ b/cpu/sam0_common/include/cpu_conf.h @@ -39,6 +39,12 @@ extern "C" { #define CPU_DEFAULT_IRQ_PRIO (1U) #define CPU_IRQ_NUMOF PERIPH_COUNT_IRQn #define CPU_FLASH_BASE FLASH_ADDR + +#ifdef CPU_SAML1X +#define CPU_FLASH_RWWEE_BASE DATAFLASH_ADDR +#else +#define CPU_FLASH_RWWEE_BASE NVMCTRL_RWW_EEPROM_ADDR +#endif /** @} */ /** @@ -59,6 +65,16 @@ extern "C" { #define FLASHPAGE_RAW_BLOCKSIZE (16) /* Writing should be always 4 byte aligned */ #define FLASHPAGE_RAW_ALIGNMENT (4) +/* Add RWWEE memory if supported by revision of the chip + * On some chips it is called RWW EEPROM while on some DATAFLASH, try to + * catch all without relying on the CPU model but on the named defines + */ +#ifdef NVMCTRL_RWW_EEPROM_SIZE +#define FLASHPAGE_RWWEE_NUMOF (NVMCTRL_RWWEE_PAGES / FLASHPAGE_PAGES_PER_ROW) +#endif +#ifdef DATAFLASH_SIZE +#define FLASHPAGE_RWWEE_NUMOF (DATAFLASH_NB_OF_PAGES / FLASHPAGE_PAGES_PER_ROW) +#endif /** @} */ #ifdef __cplusplus diff --git a/cpu/sam0_common/periph/flashpage.c b/cpu/sam0_common/periph/flashpage.c index 22ba4b8dd6..43be85fdd3 100644 --- a/cpu/sam0_common/periph/flashpage.c +++ b/cpu/sam0_common/periph/flashpage.c @@ -32,6 +32,9 @@ #define NVMCTRL_PAC_BIT (0x00000002) +#define FLASH_MAIN 0 +#define FLASH_RWWEE 1 + /** * @brief NVMCTRL selection macros */ @@ -75,7 +78,11 @@ static void _lock(void) #endif } +#ifdef FLASHPAGE_RWWEE_NUMOF +void flashpage_write_raw_internal(void *target_addr, const void *data, size_t len, int flash_type) +#else void flashpage_write_raw(void *target_addr, const void *data, size_t len) +#endif { /* The actual minimal block size for writing is 16B, thus we * assert we write on multiples and no less of that length. @@ -87,8 +94,17 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len) ((unsigned)data % FLASHPAGE_RAW_ALIGNMENT))); /* ensure the length doesn't exceed the actual flash size */ - assert(((unsigned)target_addr + len) <= - (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF))); +#ifdef FLASHPAGE_RWWEE_NUMOF + if (flash_type == FLASH_RWWEE) { + assert(((unsigned)target_addr + len) <= + (CPU_FLASH_RWWEE_BASE + (FLASHPAGE_SIZE * FLASHPAGE_RWWEE_NUMOF))); + } else { +#endif + assert(((unsigned)target_addr + len) <= + (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF))); +#ifdef FLASHPAGE_RWWEE_NUMOF + } +#endif uint32_t *dst = (uint32_t *)target_addr; const uint32_t *data_addr = data; @@ -97,22 +113,46 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len) len /= 4; _unlock(); - _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC); wait_nvm_is_ready(); for (unsigned i = 0; i < len; i++) { *dst++ = *data_addr++; } - _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP); +#ifdef FLASHPAGE_RWWEE_NUMOF + if (flash_type == FLASH_RWWEE) { +#ifdef CPU_SAML1X + /* SAML1X use the same Write Page command for both flash memories */ + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP); +#else + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_RWWEEWP); +#endif + } else { +#endif + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP); +#ifdef FLASHPAGE_RWWEE_NUMOF + } +#endif wait_nvm_is_ready(); _lock(); } +#ifdef FLASHPAGE_RWWEE_NUMOF +void flashpage_write_internal(int page, const void *data, int flash_type) +#else void flashpage_write(int page, const void *data) +#endif { - assert((uint32_t)page < FLASHPAGE_NUMOF); + uint32_t *page_addr; - uint32_t *page_addr = (uint32_t *)flashpage_addr(page); +#ifdef FLASHPAGE_RWWEE_NUMOF + if (flash_type == FLASH_RWWEE) { + page_addr = (uint32_t *)flashpage_rwwee_addr(page); + } else { +#endif + page_addr = (uint32_t *)flashpage_addr(page); +#ifdef FLASHPAGE_RWWEE_NUMOF + } +#endif /* erase given page (the ADDR register uses 16-bit addresses) */ _unlock(); @@ -122,7 +162,20 @@ void flashpage_write(int page, const void *data) #else _NVMCTRL->ADDR.reg = (((uint32_t)page_addr) >> 1); #endif - _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER); +#ifdef FLASHPAGE_RWWEE_NUMOF + if (flash_type == FLASH_RWWEE) { +#ifdef CPU_SAML1X + /* SAML1X use the same Erase command for both flash memories */ + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER); +#else + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_RWWEEER); +#endif + } else { +#endif + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER); +#ifdef FLASHPAGE_RWWEE_NUMOF + } +#endif wait_nvm_is_ready(); _lock(); @@ -136,10 +189,48 @@ void flashpage_write(int page, const void *data) * The erasing is done once as a full row is always reased. */ for (unsigned curpage = 0; curpage < FLASHPAGE_PAGES_PER_ROW; curpage++) { +#ifdef FLASHPAGE_RWWEE_NUMOF + flashpage_write_raw_internal(page_addr + (curpage * NVMCTRL_PAGE_SIZE / 4), + (void *) ((uint32_t *) data + (curpage * NVMCTRL_PAGE_SIZE / 4)), + NVMCTRL_PAGE_SIZE, flash_type); +#else flashpage_write_raw(page_addr + (curpage * NVMCTRL_PAGE_SIZE / 4), - (void *) ((uint32_t *) data + (curpage * NVMCTRL_PAGE_SIZE / 4)), - NVMCTRL_PAGE_SIZE); + (void *) ((uint32_t *) data + (curpage * NVMCTRL_PAGE_SIZE / 4)), + NVMCTRL_PAGE_SIZE); +#endif } } - } + + +#ifdef FLASHPAGE_RWWEE_NUMOF +/* + * If RWWEE flash is present then we create an additional layer for the write functions + * so we can specify the type (either MAIN or RWWEE) we want to access, keeping the + * standard API unchanged and code for systems without RWWEE at a minimum at the cost + * of some more #defines in the code + */ +void flashpage_write_raw(void *target_addr, const void *data, size_t len) +{ + flashpage_write_raw_internal(target_addr, data, len, FLASH_MAIN); +} + +void flashpage_write(int page, const void *data) +{ + assert((uint32_t)page < FLASHPAGE_NUMOF); + + flashpage_write_internal(page, data, FLASH_MAIN); +} + +void flashpage_rwwee_write_raw(void *target_addr, const void *data, size_t len) +{ + flashpage_write_raw_internal(target_addr, data, len, FLASH_RWWEE); +} + +void flashpage_rwwee_write(int page, const void *data) +{ + assert((uint32_t)page < FLASHPAGE_RWWEE_NUMOF); + + flashpage_write_internal(page, data, FLASH_RWWEE); +} +#endif diff --git a/drivers/include/periph/flashpage.h b/drivers/include/periph/flashpage.h index 68974524a8..afe3c78a75 100644 --- a/drivers/include/periph/flashpage.h +++ b/drivers/include/periph/flashpage.h @@ -31,6 +31,7 @@ * @author Hauke Petersen * @author Kaspar Schleiser * @author Francisco Acosta + * @author Federico Pellegrin * */ @@ -39,6 +40,7 @@ #include +#include "cpu_conf.h" #include "periph_cpu.h" #ifdef __cplusplus @@ -190,6 +192,122 @@ int flashpage_verify(int page, const void *data); */ int flashpage_write_and_verify(int page, const void *data); +/** + * @brief Functions to support additional RWWEE flash section + * + * Some CPUs (ie. SAML21, SAML11) have an additional separated flash section + * named RWWEE (Read While Write EEPROM Emulation) that can be accessed from + * RIOT in a similar fashion but with a set of functions ending with a + * _rwwee name variant. + * + * The support of this feature is determined by the FLASHPAGE_RWWEE_NUMOF + * define that, if defined, contains the number of RWWEE sectors in the CPU. + * This is defined in the CPU specific headers (ie. for the ATMEL SAM family + * in cpu/sam0_common/include/cpu_conf.h) + */ +#ifdef FLASHPAGE_RWWEE_NUMOF + +/** + * @brief Translate the given RWEE page number into the page's starting address + * + * @note The given page MUST be valid, otherwise the returned address points + * to an undefined memory location! + * + * @param[in] page RWWEE page number to get the address of + * + * @return starting memory address of the given RWWEE page + */ +static inline void *flashpage_rwwee_addr(int page) +{ + return (void *)(CPU_FLASH_RWWEE_BASE + (page * FLASHPAGE_SIZE)); +} + +/** + * @brief Translate the given address into the corresponding RWWEE page number + * + * The given address can be any address inside a RWWEE page. + * + * @note The given address MUST be a valid RWWEE flash address! + * + * @param[in] addr address inside the targeted RWWEE page + * + * @return RWWEE page containing the given address + */ +static inline int flashpage_rwwee_page(void *addr) +{ + return (int)(((int)addr - CPU_FLASH_RWWEE_BASE) / FLASHPAGE_SIZE); +} + +/** + * @brief Write the given RWWEE page with the given data + * + * @param[in] page RWWEE page to write + * @param[in] data data to write to the RWWEE page, MUST be FLASHPAGE_SIZE + * byte. Set to NULL for RWWEE page erase only. + */ +void flashpage_rwwee_write(int page, const void *data); + +/** + * @brief Write any number of data bytes to a given location in the + * RWWEE 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 RWWEE 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 RWWEE flash + * memory size. + */ +void flashpage_rwwee_write_raw(void *target_addr, const void *data, size_t len); + +/** + * @brief Read the given RWWEE page into the given memory location + * + * @param[in] page RWWEE page to read + * @param[out] data memory to write the RWWEE page to, MUST be FLASHPAGE_SIZE + * byte + */ +void flashpage_rwwee_read(int page, void *data); + +/** + * @brief Verify the given RWWEE page against the given data + * + * @param[in] page RWWEE page to verify + * @param[in] data data to compare RWWEE page against, MUST be FLASHPAGE_SIZE + * byte of data + * + * @return FLASHPAGE_OK if data in the RWWEE page is identical to @p data + * @return FLASHPAGE_NOMATCH if data and RWWEE page content diverge + */ +int flashpage_rwwee_verify(int page, const void *data); + +/** + * @brief Write the given RWWEE page and verify the results + * + * This is a convenience function wrapping flashpage_rwwee_write and + * flashpage_rwwee_verify. + * + * @param[in] page RWWEE page to write + * @param[in] data data to write to the RWWEE page, MUST be FLASHPAGE_SIZE + * byte. + * + * @return FLASHPAGE_OK on success + * @return FLASHPAGE_NOMATCH if data and RWWEE page content diverge + */ +int flashpage_rwwee_write_and_verify(int page, const void *data); + +#endif /* FLASHPAGE_RWWEE_NUMOF */ + #ifdef __cplusplus } #endif diff --git a/drivers/periph_common/flashpage.c b/drivers/periph_common/flashpage.c index d5c557576f..3fe1b8c6d4 100644 --- a/drivers/periph_common/flashpage.c +++ b/drivers/periph_common/flashpage.c @@ -53,4 +53,34 @@ int flashpage_write_and_verify(int page, const void *data) return flashpage_verify(page, data); } + +#if defined(FLASHPAGE_RWWEE_NUMOF) + +void flashpage_rwwee_read(int page, void *data) +{ + assert(page < (int)FLASHPAGE_RWWEE_NUMOF); + + memcpy(data, flashpage_rwwee_addr(page), FLASHPAGE_SIZE); +} + +int flashpage_rwwee_verify(int page, const void *data) +{ + assert(page < (int)FLASHPAGE_RWWEE_NUMOF); + + if (memcmp(flashpage_rwwee_addr(page), data, FLASHPAGE_SIZE) == 0) { + return FLASHPAGE_OK; + } + else { + return FLASHPAGE_NOMATCH; + } +} + +int flashpage_rwwee_write_and_verify(int page, const void *data) +{ + flashpage_rwwee_write(page, data); + return flashpage_rwwee_verify(page, data); +} + +#endif + #endif diff --git a/tests/periph_flashpage/Makefile b/tests/periph_flashpage/Makefile index b64e980f4d..7c21e1b154 100644 --- a/tests/periph_flashpage/Makefile +++ b/tests/periph_flashpage/Makefile @@ -3,6 +3,7 @@ include ../Makefile.tests_common FEATURES_REQUIRED += periph_flashpage FEATURES_OPTIONAL += periph_flashpage_raw +FEATURES_OPTIONAL += periph_flashpage_rwee USEMODULE += shell diff --git a/tests/periph_flashpage/main.c b/tests/periph_flashpage/main.c index b226969ed0..b70e6b94f2 100644 --- a/tests/periph_flashpage/main.c +++ b/tests/periph_flashpage/main.c @@ -31,7 +31,6 @@ /* When writing raw bytes on flash, data must be correctly aligned. */ #ifdef MODULE_PERIPH_FLASHPAGE_RAW #define ALIGNMENT_ATTR __attribute__ ((aligned (FLASHPAGE_RAW_ALIGNMENT))) - /* * @brief Allocate an aligned buffer for raw writings */ @@ -102,6 +101,11 @@ static int cmd_info(int argc, char **argv) printf("Page size:\t\t%i\n", (int)FLASHPAGE_SIZE); printf("Number of pages:\t%i\n", (int)FLASHPAGE_NUMOF); +#ifdef FLASHPAGE_RWWEE_NUMOF + printf("RWWEE Flash start addr:\t0x%08x\n", (int)CPU_FLASH_RWWEE_BASE); + printf("RWWEE Number of pages:\t%i\n", (int)FLASHPAGE_RWWEE_NUMOF); +#endif + return 0; } @@ -351,6 +355,162 @@ static int cmd_test_last_raw(int argc, char **argv) } #endif + +#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; +} + +#ifdef MODULE_PERIPH_FLASHPAGE_RAW +/** + * @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(((int)FLASHPAGE_RWWEE_NUMOF - 1), NULL); + + flashpage_rwwee_write_raw(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 + +#endif + static const shell_command_t shell_commands[] = { { "info", "Show information about pages", cmd_info }, { "dump", "Dump the selected page to STDOUT", cmd_dump }, @@ -366,6 +526,15 @@ static const shell_command_t shell_commands[] = { { "test_last", "Write and verify test pattern on last page available", cmd_test_last }, #ifdef MODULE_PERIPH_FLASHPAGE_RAW { "test_last_raw", "Write and verify raw short write on last page available", cmd_test_last_raw }, +#endif +#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 }, +#ifdef MODULE_PERIPH_FLASHPAGE_RAW + { "test_last_rwwee_raw", "Write and verify raw short write on last RWWEE page available", cmd_test_last_rwwee_raw }, +#endif #endif { NULL, NULL, NULL } };