diff --git a/cpu/stm32/Makefile.features b/cpu/stm32/Makefile.features index 6e3c46ad99..b294249711 100644 --- a/cpu/stm32/Makefile.features +++ b/cpu/stm32/Makefile.features @@ -16,6 +16,11 @@ ifneq (,$(filter $(CPU_FAM),f0 f1 f3 g0 g4 l0 l1 l4 l5 wb)) FEATURES_PROVIDED += periph_flashpage_pagewise endif +# The f2, f4 and f7 do not support the pagewise api +ifneq (,$(filter $(CPU_FAM),f2 f4 f7)) + FEATURES_PROVIDED += periph_flashpage +endif + ifneq (,$(filter $(CPU_FAM),l0 l1)) FEATURES_PROVIDED += periph_eeprom endif diff --git a/cpu/stm32/include/cpu_conf.h b/cpu/stm32/include/cpu_conf.h index d087e4c65f..fdc37fbb7f 100644 --- a/cpu/stm32/include/cpu_conf.h +++ b/cpu/stm32/include/cpu_conf.h @@ -116,7 +116,73 @@ extern "C" { #define FLASHPAGE_SIZE (128U) #endif +#ifdef FLASHPAGE_SIZE #define FLASHPAGE_NUMOF (STM32_FLASHSIZE / FLASHPAGE_SIZE) +#endif + +#if defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) +#define PERIPH_FLASHPAGE_CUSTOM_PAGESIZES + +/** + * @brief stm32 dual bank configuration + * + * By default, the stm32f4 series with 1MB flash enable the DB1M flag to split + * the 1MB flash into two banks, 2MB devices are always split in two banks. + * On both the stm32f4 and the stm32f7 this can be modified with user + * programmable flags. Detecting the settings at runtime is not supported + * + * @note This must match the setting on the MCU. by default it is assumed that + * the user has not changed this setting manually. + */ +#if (defined(FLASH_OPTCR_DB1M) && (STM32_FLASHSIZE >= (1024 * 1024))) +#define FLASHPAGE_DUAL_BANK 1 +#else +#define FLASHPAGE_DUAL_BANK 0 +#endif + +/* stm32f7 uses single bank with 32KB to 256KB sectors on a number of devices */ +#if defined(CPU_FAM_STM32F7) +#if defined(CPU_LINE_STM32F745xx) || \ + defined(CPU_LINE_STM32F746xx) || \ + defined(CPU_LINE_STM32F750xx) || \ + defined(CPU_LINE_STM32F756xx) || \ + defined(CPU_LINE_STM32F765xx) || \ + defined(CPU_LINE_STM32F767xx) || \ + defined(CPU_LINE_STM32F769xx) || \ + defined(CPU_LINE_STM32F777xx) || \ + defined(CPU_LINE_STM32F779xx) +#define FLASHPAGE_MIN_SECTOR_SIZE (32 * 1024) +#elif defined(CPU_LINE_STM32F722xx) || \ + defined(CPU_LINE_STM32F723xx) || \ + defined(CPU_LINE_STM32F730xx) || \ + defined(CPU_LINE_STM32F732xx) || \ + defined(CPU_LINE_STM32F733xx) +#define FLASHPAGE_MIN_SECTOR_SIZE (16 * 1024) +#else +/* Intentionally error on an unknown line to prevent flashpage errors */ +#error Unknown STM32F7 Line, unable to determine FLASHPAGE_MIN_SECTOR_SIZE +#endif + +#else /* CPU_FAM_STM32F7 */ +#define FLASHPAGE_MIN_SECTOR_SIZE (16 * 1024) +#endif + +#if FLASHPAGE_DUAL_BANK +/* Number of "large" sectors + 4 for the small sectors that together equal a + * single large sector. Times two to account for the two banks */ +#define FLASHPAGE_NUMOF ((STM32_FLASHSIZE / \ + (8 * FLASHPAGE_MIN_SECTOR_SIZE)) + 8) +#else +/* Number of "large" sectors + 4 for the small sectors that together equal a + * single large sector, eg: 1 MB = 7 * 128 KB sectors + 1 64 KB and 4 16 KB + * sectors */ +#define FLASHPAGE_NUMOF ((STM32_FLASHSIZE / \ + (8 * FLASHPAGE_MIN_SECTOR_SIZE)) + 4) +#endif + +#endif + /* The minimum block size which can be written depends on the family. * However, the erase block is always FLASHPAGE_SIZE. @@ -124,13 +190,15 @@ extern "C" { #if defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \ defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32G0) || \ defined(CPU_FAM_STM32L5) -#define FLASHPAGE_WRITE_BLOCK_SIZE (8U) +#define FLASHPAGE_WRITE_BLOCK_SIZE (8U) typedef uint64_t stm32_flashpage_block_t; -#elif defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) -#define FLASHPAGE_WRITE_BLOCK_SIZE (4U) +#elif defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) || \ + defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) +#define FLASHPAGE_WRITE_BLOCK_SIZE (4U) typedef uint32_t stm32_flashpage_block_t; #else -#define FLASHPAGE_WRITE_BLOCK_SIZE (2U) +#define FLASHPAGE_WRITE_BLOCK_SIZE (2U) typedef uint16_t stm32_flashpage_block_t; #endif diff --git a/cpu/stm32/kconfigs/f2/Kconfig b/cpu/stm32/kconfigs/f2/Kconfig index f99960816f..8956fbfa18 100644 --- a/cpu/stm32/kconfigs/f2/Kconfig +++ b/cpu/stm32/kconfigs/f2/Kconfig @@ -11,6 +11,7 @@ config CPU_FAM_F2 select CPU_CORE_CORTEX_M3 select HAS_CPU_STM32F2 select HAS_CORTEXM_MPU + select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_HWRNG select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/f4/Kconfig b/cpu/stm32/kconfigs/f4/Kconfig index e17e866365..05eddcd7d9 100644 --- a/cpu/stm32/kconfigs/f4/Kconfig +++ b/cpu/stm32/kconfigs/f4/Kconfig @@ -11,6 +11,7 @@ config CPU_FAM_F4 select CPU_CORE_CORTEX_M4F select HAS_CPU_STM32F4 select HAS_CORTEXM_MPU + select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/f7/Kconfig b/cpu/stm32/kconfigs/f7/Kconfig index 9a1d4c8094..0a8303d843 100644 --- a/cpu/stm32/kconfigs/f7/Kconfig +++ b/cpu/stm32/kconfigs/f7/Kconfig @@ -11,6 +11,7 @@ config CPU_FAM_F7 select CPU_CORE_CORTEX_M7 select HAS_CPU_STM32F7 select HAS_CORTEXM_MPU + select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_HWRNG select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/periph/flash_common.c b/cpu/stm32/periph/flash_common.c index 6c8b517bea..ca6faf5748 100644 --- a/cpu/stm32/periph/flash_common.c +++ b/cpu/stm32/periph/flash_common.c @@ -40,7 +40,9 @@ #define FLASH_SR_EOP (FLASH_NSSR_NSEOP) #else #if defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \ - defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32G0) + defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32G0) || \ + defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) #define FLASH_KEY1 ((uint32_t)0x45670123) #define FLASH_KEY2 ((uint32_t)0xCDEF89AB) #endif diff --git a/cpu/stm32/periph/flashpage.c b/cpu/stm32/periph/flashpage.c index 1adbea3810..49dcd4b7b0 100644 --- a/cpu/stm32/periph/flashpage.c +++ b/cpu/stm32/periph/flashpage.c @@ -47,6 +47,13 @@ #define FLASH_CR_PER (FLASH_NSCR_NSPER) #define FLASH_CR_BKER (FLASH_NSCR_NSBKER) #define FLASH_CR_PG (FLASH_NSCR_NSPG) +#elif defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) +#define FLASHPAGE_DIV (4U) +#define FLASH_CR_PER (FLASH_CR_SER) +#define FLASH_CR_PNB (FLASH_CR_SNB) +#define FLASH_CR_PNB_Pos (FLASH_CR_SNB_Pos) +#define CNTRL_REG (FLASH->CR) #else #define CNTRL_REG (FLASH->CR) #define CNTRL_REG_LOCK (FLASH_CR_LOCK) @@ -102,7 +109,8 @@ static void _erase_page(void *page_addr) *(uint32_t *)page_addr = 0; #elif defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \ defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32G0) || \ - defined(CPU_FAM_STM32L5) + defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32F2) || \ + defined(CPU_FAM_STM32F4) || defined(CPU_FAM_STM32F7) DEBUG("[flashpage] erase: setting the page address\n"); uint8_t pn; #if (FLASHPAGE_NUMOF <= MAX_PAGES_PER_BANK) || defined(CPU_FAM_STM32WB) @@ -118,8 +126,19 @@ static void _erase_page(void *page_addr) pn = (uint8_t)page; #endif CNTRL_REG &= ~FLASH_CR_PNB; +#if FLASHPAGE_DUAL_BANK + if (pn > (FLASHPAGE_NUMOF / 2 - 1)) { + pn = pn - (FLASHPAGE_NUMOF / 2); + CNTRL_REG |= FLASH_CR_SNB_4 | (uint32_t)(pn << FLASH_CR_PNB_Pos); + } + else { + CNTRL_REG |= (uint32_t)(pn << FLASH_CR_PNB_Pos); + } +#else CNTRL_REG |= (uint32_t)(pn << FLASH_CR_PNB_Pos); +#endif CNTRL_REG |= FLASH_CR_STRT; + DEBUG("[flashpage] erase: the page address is set and started\n"); #else /* CPU_FAM_STM32F0 || CPU_FAM_STM32F1 || CPU_FAM_STM32F3 */ DEBUG("[flashpage] erase: setting the page address\n"); FLASH->AR = (uint32_t)page_addr; @@ -130,9 +149,26 @@ static void _erase_page(void *page_addr) /* wait as long as device is busy */ _wait_for_pending_operations(); - /* reset PER bit */ +#ifdef FLASH_ACR_DCEN /* Flush the data cache after page erase */ + if (FLASH->ACR & FLASH_ACR_DCEN) { + FLASH->ACR &= ~FLASH_ACR_DCEN; + FLASH->ACR |= FLASH_ACR_DCRST; + FLASH->ACR |= FLASH_ACR_DCEN; + } +#endif +#ifdef FLASH_ACR_ICEN /* Flush the instruction cache after page erase */ + if (FLASH->ACR & FLASH_ACR_ICEN) { + FLASH->ACR &= ~FLASH_ACR_ICEN; + FLASH->ACR |= FLASH_ACR_ICRST; + FLASH->ACR |= FLASH_ACR_ICEN; + } +#endif + +#ifdef FLASH_CR_PNB + /* reset PER bit (if the register settings exist) */ DEBUG("[flashpage] erase: resetting the page erase bit\n"); - CNTRL_REG &= ~(FLASH_CR_PER); + CNTRL_REG &= ~(FLASH_CR_PER | FLASH_CR_PNB); +#endif /* lock the flash module again */ _lock(); @@ -172,8 +208,8 @@ void flashpage_write(void *target_addr, const void *data, size_t len) ((unsigned)data % FLASHPAGE_WRITE_BLOCK_ALIGNMENT))); /* ensure the length doesn't exceed the actual flash size */ - assert(((unsigned)target_addr + len) < - (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF)) + 1); + assert(((uintptr_t)(target_addr) + len) < + (uintptr_t)flashpage_addr(FLASHPAGE_NUMOF + 1)); stm32_flashpage_block_t *dst = target_addr; const stm32_flashpage_block_t *data_addr = data; @@ -185,9 +221,28 @@ void flashpage_write(void *target_addr, const void *data, size_t len) stmclk_enable_hsi(); #endif +#ifdef FLASH_ACR_DCEN + /* Disable the data cache during page writes */ + bool data_cache = FLASH->ACR & FLASH_ACR_DCEN; + if (data_cache) { + FLASH->ACR &= ~FLASH_ACR_DCEN; + } +#endif +#ifdef FLASH_ACR_ICEN + /* Disable the instruction cache during page writes */ + bool instruction_cache = FLASH->ACR & FLASH_ACR_ICEN; + if (instruction_cache) { + FLASH->ACR &= ~FLASH_ACR_ICEN; + } +#endif + /* unlock the flash module */ _unlock_flash(); +#ifdef FLASH_CR_PSIZE_1 + CNTRL_REG |= FLASH_CR_PSIZE_1; /* Word size parallelism */ +#endif + /* make sure no flash operation is ongoing */ _wait_for_pending_operations(); @@ -195,13 +250,18 @@ void flashpage_write(void *target_addr, const void *data, size_t len) #if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F1) || \ defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L4) || \ defined(CPU_FAM_STM32WB) || defined(CPU_FAM_STM32G4) || \ - defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) + defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \ + defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) /* set PG bit and program page to flash */ CNTRL_REG |= FLASH_CR_PG; #endif for (size_t i = 0; i < (len / sizeof(stm32_flashpage_block_t)); i++) { DEBUG("[flashpage_raw] writing %c to %p\n", (char)data_addr[i], dst); *dst++ = data_addr[i]; +#if defined(CPU_FAM_STM32F7) + __DMB(); +#endif /* wait as long as device is busy */ _wait_for_pending_operations(); } @@ -210,7 +270,9 @@ void flashpage_write(void *target_addr, const void *data, size_t len) #if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F1) || \ defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L4) || \ defined(CPU_FAM_STM32WB) || defined(CPU_FAM_STM32G4) || \ - defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) + defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \ + defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) CNTRL_REG &= ~(FLASH_CR_PG); #endif DEBUG("[flashpage_raw] write: done writing data\n"); @@ -218,6 +280,21 @@ void flashpage_write(void *target_addr, const void *data, size_t len) /* lock the flash module again */ _lock(); +#ifdef FLASH_ACR_DCEN + /* Enable the data cache if it was enabled before. Always reset it */ + FLASH->ACR |= FLASH_ACR_DCRST; + if (data_cache) { + FLASH->ACR |= FLASH_ACR_DCEN; + } +#endif +#ifdef FLASH_ACR_ICEN + /* Enable the instruction cache if it was enabled before. Always reset it */ + FLASH->ACR |= FLASH_ACR_ICRST; + if (instruction_cache) { + FLASH->ACR |= FLASH_ACR_ICEN; + } +#endif + #if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F1) || \ defined(CPU_FAM_STM32F3) /* restore the HSI state */ @@ -226,3 +303,54 @@ void flashpage_write(void *target_addr, const void *data, size_t len) } #endif } + +#if defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) +size_t flashpage_size(unsigned page) +{ + if (page < 4) { + return FLASHPAGE_MIN_SECTOR_SIZE; + } + else if (page == 4) { + return 4 * FLASHPAGE_MIN_SECTOR_SIZE; + } + else { + return 8 * FLASHPAGE_MIN_SECTOR_SIZE; + } +} + +void *flashpage_addr(unsigned page) +{ + uintptr_t addr = CPU_FLASH_BASE; + while (page) { + addr += flashpage_size(--page); + } + + return (void*)addr; +} + +unsigned flashpage_page(void *addr) +{ + /* Calculates the flashpage number based on the address for the + * non-homogeneous flashpage stm32 series. + * These all follow the same pattern of 4 sectors of base size, 1 sector of + * 4 times the base size and the rest of the pages are 8 times the base + * size. Here we calculate the page number as if all pages are of base size + * and then compensate for the larger sectors */ + unsigned page = (((intptr_t)addr - CPU_FLASH_BASE) / + FLASHPAGE_MIN_SECTOR_SIZE); + + /* check if beyond the 4 base sectors + the 4 * base size sector */ + if (page > 7) { + /* Divide by 8 and compensate for the initial 5 sectors */ + page = (page / 8) + 4; + } + /* If the page number is between 4 and 7 (inclusive), the address is in the + * single 4 * base size sector */ + else if (page > 3) { + page = 4; + } + + return page; +} +#endif diff --git a/tests/periph_flashpage/main.c b/tests/periph_flashpage/main.c index 1a42f60d7e..27eb769483 100644 --- a/tests/periph_flashpage/main.c +++ b/tests/periph_flashpage/main.c @@ -45,6 +45,7 @@ */ static char raw_buf[64] ALIGNMENT_ATTR; +#ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE /** * @brief Allocate space for 1 flash page in RAM * @@ -54,6 +55,7 @@ static char raw_buf[64] ALIGNMENT_ATTR; * requires 64 bit alignment. */ static uint8_t page_mem[FLASHPAGE_SIZE] ALIGNMENT_ATTR; +#endif static int getpage(const char *str) { @@ -65,6 +67,7 @@ static int getpage(const char *str) return page; } +#ifdef FLASHPAGE_SIZE static void dumpchar(uint8_t mem) { if (mem >= ' ' && mem <= '~') { @@ -97,6 +100,7 @@ static void dump_local(void) puts("Local page buffer:"); memdump(page_mem, FLASHPAGE_SIZE); } +#endif static int cmd_info(int argc, char **argv) { @@ -104,7 +108,11 @@ static int cmd_info(int argc, char **argv) (void)argv; printf("Flash start addr:\t0x%08x\n", (int)CPU_FLASH_BASE); +#ifdef FLASHPAGE_SIZE printf("Page size:\t\t%i\n", (int)FLASHPAGE_SIZE); +#else + puts("Page size:\t\tvariable"); +#endif printf("Number of pages:\t%i\n", (int)FLASHPAGE_NUMOF); #ifdef FLASHPAGE_RWWEE_NUMOF @@ -120,6 +128,7 @@ static int cmd_info(int argc, char **argv) return 0; } +#ifdef FLASHPAGE_SIZE static int cmd_dump(int argc, char **argv) { int page; @@ -137,7 +146,7 @@ static int cmd_dump(int argc, char **argv) addr = flashpage_addr(page); printf("Flash page %i at address %p\n", page, addr); - memdump(addr, FLASHPAGE_SIZE); + memdump(addr, flashpage_size(page)); return 0; } @@ -172,6 +181,7 @@ static int cmd_read(int argc, char **argv) return 0; } +#endif #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE static int cmd_write(int argc, char **argv) @@ -258,6 +268,7 @@ static int cmd_erase(int argc, char **argv) return 0; } +#ifdef FLASHPAGE_SIZE static int cmd_edit(int argc, char **argv) { int offset; @@ -283,6 +294,7 @@ static int cmd_edit(int argc, char **argv) return 0; } +#endif #ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE static int cmd_test(int argc, char **argv) @@ -614,16 +626,16 @@ static int cmd_test_config(int argc, char **argv) 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 }, -#ifdef MODULE_PERIPH_FLASHPAGE_PAGEWISE { "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 }, - { "edit", "Write bytes to the local page buffer", cmd_edit }, #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 diff --git a/tests/unittests/tests-flashpage/tests-flashpage.c b/tests/unittests/tests-flashpage/tests-flashpage.c index 4a37ac386b..00ddd6c7cc 100644 --- a/tests/unittests/tests-flashpage/tests-flashpage.c +++ b/tests/unittests/tests-flashpage/tests-flashpage.c @@ -27,6 +27,9 @@ #define FLASHPAGE_NUMOF 128 #endif +/* fake uniform flashpage sizes for devices that don't have it */ +#undef PERIPH_FLASHPAGE_CUSTOM_PAGESIZES + #include "periph/flashpage.h" static void test_flashbase_addr(void)