diff --git a/cpu/stm32_common/periph/flash_common.c b/cpu/stm32_common/periph/flash_common.c index eeeb3b27fc39..94af61eb7207 100644 --- a/cpu/stm32_common/periph/flash_common.c +++ b/cpu/stm32_common/periph/flash_common.c @@ -32,7 +32,8 @@ #define CNTRL_REG_LOCK (FLASH_PECR_PELOCK) #define KEY_REG (FLASH->PEKEYR) #else -#if defined(CPU_FAM_STM32L4) +#if defined(CPU_FAM_STM32L4) || 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_common/periph/flashpage.c b/cpu/stm32_common/periph/flashpage.c index e95fc9ec0b15..611809879319 100644 --- a/cpu/stm32_common/periph/flashpage.c +++ b/cpu/stm32_common/periph/flashpage.c @@ -42,6 +42,10 @@ #else #if defined(CPU_FAM_STM32L4) #define FLASHPAGE_DIV (8U) +#elif defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) +#define FLASHSECTORS_BANK (12) +#define FLASHPAGE_DIV (4U) #else #define FLASHPAGE_DIV (2U) #endif @@ -53,6 +57,7 @@ extern void _lock(void); extern void _unlock(void); extern void _wait_for_pending_operations(void); + static void _unlock_flash(void) { _unlock(); @@ -69,6 +74,129 @@ static void _unlock_flash(void) #endif } +#if defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) +static inline void * _flashsector_addr(uint8_t sn) +{ +#if (FLASH_DUAL_BANK == 1) + uint32_t addr = CPU_FLASH_BASE + (STM32_FLASHSIZE / 2); +#else + uint32_t addr = CPU_FLASH_BASE; +#endif + if (sn <= 4) { + addr += (FLASHSECTOR_SIZE_MIN * sn); + } + else { + addr += (FLASHSECTOR_SIZE_MIN * (sn - 4)); + } + return (void *) addr; +} + +static inline int _flashbank_sector(void *addr) +{ + /* When flash is in single bank there is a max of 12 sectors. The first 4 + sectors are equally sized, the 5th amount to the sum of the first 4 + sectors and the 6th to 12th amount to the sum of the first 5 sectos. + e.g.: if FLASHSECTOR_SIZE_MIN is 16k there are 4 sectors of 16kB, + 1 of 64kB and 7 of 128kB. */ + /* We use this pattern to find in what sector and address falls */ + uint8_t sn = + (uint8_t)(((uint32_t)addr - CPU_FLASH_BASE) / FLASHSECTOR_SIZE_MIN); + + if (sn > 3 && sn < 8) { + sn = 4; + } + else if (sn >= 8) { + sn = (sn / 8) + 4; + } + return sn; +} + +static inline int _flashsector_sector(void *addr) +{ + /* When in dual bank there can be up to 24 sectors, where sectors 12-23 + follow the same layout as sectors 0-11 */ +#if (FLASH_DUAL_BANK == 1) + if ((uint32_t)addr >= (STM32_FLASHSIZE / 2) + CPU_FLASH_BASE) { + DEBUG("[flashsector]: dual bank sector \n"); + addr = (void *)((uint32_t)addr - (STM32_FLASHSIZE / 2)); + return FLASHSECTORS_BANK + _flashbank_sector(addr); + } + else { + DEBUG("[flashsector]: single bank sector \n"); + return _flashbank_sector(addr); + } +#else + return _flashbank_sector(addr); +#endif +} + +static void _erase_sector(uint8_t sn) +{ + /* make sure no flash operation is ongoing */ + _wait_for_pending_operations(); + + /* unlock the flash module */ + _unlock_flash(); + + /* set parallelism to 32bits */ + CNTRL_REG &= FLASH_CR_PSIZE_Msk; + CNTRL_REG |= (0x2 << FLASH_CR_PSIZE_Pos); + + /* make sure no flash operation is ongoing */ + _wait_for_pending_operations(); + + DEBUG("[flashsector] erase: setting the sector erase code\n"); + CNTRL_REG |= ((sn % FLASHSECTORS_BANK) << FLASH_CR_SNB_Pos); +#if (FLASH_DUAL_BANK == 1) + CNTRL_REG |= (sn / FLASHSECTORS_BANK) * FLASH_CR_SNB_4; +#endif + DEBUG("[flashsector] erase: setting the erase bit\n"); + CNTRL_REG |= FLASH_CR_SER; + + DEBUG("[flashsector] erase: trigger the page erase\n"); + CNTRL_REG |= FLASH_CR_STRT; + + /* wait as long as device is busy */ + _wait_for_pending_operations(); + + /* reset PER bit */ + DEBUG("[flashsector] erase: resetting the sector erase bit\n"); + CNTRL_REG &= ~FLASH_CR_SER; + + /* lock the flash module again */ + _lock(); +} + +static void _erase_sector_page(void *page_addr) +{ + DEBUG("[flashsector] erase: address to erase: %p\n", page_addr); + uint8_t sn = _flashsector_sector(page_addr); + /* always erase sector when writing to first page */ + if(_flashsector_addr(sn) == page_addr) { + DEBUG("[flashsector] erase: erasing sector: %d\n", sn); + _erase_sector(sn); + return; + } + /* avoid erasing whole sector if "page" is blank */ + bool blank = true; + for (unsigned i = 0; i < FLASHPAGE_SIZE; i += sizeof(uint32_t)) { + if (*(uint32_t *)(page_addr + i) != 0xffffffff) { + blank = false; + break; + } + } + /* erase the sector if it failed the blank check */ + if (!blank) { + DEBUG("[flashsector] erase: erasing sector: %d\n", sn); + _erase_sector(sn); + } +} +#endif + +#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F1) || \ + defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) || \ + defined(CPU_FAM_STM32L4) static void _erase_page(void *page_addr) { #if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) || \ @@ -140,6 +268,7 @@ static void _erase_page(void *page_addr) } #endif } +#endif void flashpage_write_raw(void *target_addr, const void *data, size_t len) { @@ -155,7 +284,9 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len) assert(((unsigned)target_addr + len) < (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF)) + 1); -#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) +#if defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L0) || \ + defined(CPU_FAM_STM32L1) uint32_t *dst = target_addr; const uint32_t *data_addr = data; #elif defined(CPU_FAM_STM32L4) @@ -179,22 +310,36 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len) /* make sure no flash operation is ongoing */ _wait_for_pending_operations(); +#if defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4)|| \ + defined(CPU_FAM_STM32F7) + /* set parallelism to 32bits */ + CNTRL_REG &= FLASH_CR_PSIZE_Msk; + CNTRL_REG |= (0x2 << FLASH_CR_PSIZE_Pos); +#endif + DEBUG("[flashpage_raw] write: now writing the data\n"); #if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F1) || \ - defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L4) + defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F3) || \ + defined(CPU_FAM_STM32F4) || defined(CPU_FAM_STM32F7) || \ + defined(CPU_FAM_STM32L4) /* set PG bit and program page to flash */ CNTRL_REG |= FLASH_CR_PG; #endif for (size_t i = 0; i < (len / FLASHPAGE_DIV); i++) { DEBUG("[flashpage_raw] writing %c to %p\n", (char)data_addr[i], dst); *dst++ = data_addr[i]; +#if defined(CPU_FAM_STM32F7) + __DSB(); +#endif /* wait as long as device is busy */ _wait_for_pending_operations(); } /* clear program bit again */ #if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F1) || \ - defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L4) + defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F3) || \ + defined(CPU_FAM_STM32F4) || defined(CPU_FAM_STM32F7) || \ + defined(CPU_FAM_STM32L4) CNTRL_REG &= ~(FLASH_CR_PG); #endif DEBUG("[flashpage_raw] write: done writing data\n"); @@ -226,8 +371,12 @@ void flashpage_write(int page, const void *data) #endif /* ERASE sequence */ +#if defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) + _erase_sector_page(page_addr); +#else _erase_page(page_addr); - +#endif /* WRITE sequence */ if (data != NULL) { flashpage_write_raw(page_addr, data, FLASHPAGE_SIZE); diff --git a/cpu/stm32f2/Makefile.features b/cpu/stm32f2/Makefile.features index c51e0186eb8d..1549e4ac5def 100644 --- a/cpu/stm32f2/Makefile.features +++ b/cpu/stm32f2/Makefile.features @@ -1,3 +1,5 @@ FEATURES_PROVIDED += periph_hwrng +FEATURES_PROVIDED += periph_flashpage +FEATURES_PROVIDED += periph_flashpage_raw -include $(RIOTCPU)/stm32_common/Makefile.features diff --git a/cpu/stm32f2/include/cpu_conf.h b/cpu/stm32f2/include/cpu_conf.h index 745d9f8f04bd..6d26878ce554 100644 --- a/cpu/stm32f2/include/cpu_conf.h +++ b/cpu/stm32f2/include/cpu_conf.h @@ -38,6 +38,38 @@ extern "C" { #define CPU_FLASH_BASE FLASH_BASE /** @} */ +/** + * @brief Flash sector configuration + * + * @{ + */ +#define FLASH_DUAL_BANK (0) +#define FLASHSECTOR_SIZE_MIN (16*1024U) +/** @} */ + +/** + * @brief Flash page configuration + * + * NOTE: STM32F2 flash is organized in sectors, FLASHPAGE_* is + * defined as a wrapper over sectors. + * + * @{ + */ +/* To keep the same flashpage functionality an arbitrary 1K < FLASHSECTOR_SIZE_MIN + * (size of smaller sector) is defined + */ +/* An erase byte in flash is set to 0xff */ +#define FLASH_ERASE_STATE (0xff) +#define FLASHPAGE_SIZE (1024U) +#define FLASHPAGE_NUMOF (STM32_FLASHSIZE / FLASHPAGE_SIZE) +/* The minimum block size which can be written is 4B. However, the erase + * depends on the specific sector. + */ +#define FLASHPAGE_RAW_BLOCKSIZE (4U) +/* Writing should be always 4 bytes aligned */ +#define FLASHPAGE_RAW_ALIGNMENT (4U) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/stm32f4/Makefile.features b/cpu/stm32f4/Makefile.features index f6818fb224c7..9ddb155deff8 100644 --- a/cpu/stm32f4/Makefile.features +++ b/cpu/stm32f4/Makefile.features @@ -1,4 +1,6 @@ FEATURES_PROVIDED += periph_hwrng +FEATURES_PROVIDED += periph_flashpage +FEATURES_PROVIDED += periph_flashpage_raw # the granularity of provided feature definition for STMs is currently by CPU # sub-family (e.g., stm32f[1234]). Unfortunately, only some of e.g., the diff --git a/cpu/stm32f4/include/cpu_conf.h b/cpu/stm32f4/include/cpu_conf.h index 10918d47f2ed..96ef32044fcb 100644 --- a/cpu/stm32f4/include/cpu_conf.h +++ b/cpu/stm32f4/include/cpu_conf.h @@ -55,6 +55,41 @@ extern "C" { #define CPU_FLASH_BASE FLASH_BASE /** @} */ +/** + * @brief Flash sector configuration + * + * @{ + */ +#if (defined(CPU_LINE_STM32F429xx) || defined(CPU_LINE_STM32F437xx)) && \ + (STM32_FLASHSIZE == (2048*1024)) +#define FLASH_DUAL_BANK (1) +#else +#define FLASH_DUAL_BANK (0) +#endif +#define FLASHSECTOR_SIZE_MIN (16*1024U) +/** @} */ + +/** + * @brief Flash page configuration + * + * NOTE: STM32F4 flash is organized in sectors, FLASHPAGE_* is + * defined as a wrapper over sectors. + * + * @{ + */ +/* To keep the same flashpage functionality an arbitrary 1K < FLASHSECTOR_SIZE_MIN + * (size of smaller sector) is defined + */ +#define FLASHPAGE_SIZE (1024) +#define FLASHPAGE_NUMOF (STM32_FLASHSIZE / FLASHPAGE_SIZE) +/* The minimum block size which can be written is 4B. However, the erase + * depends on the specific sector. + */ +#define FLASHPAGE_RAW_BLOCKSIZE (4U) +/* Writing should be always 4 bytes aligned */ +#define FLASHPAGE_RAW_ALIGNMENT (4U) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/stm32f7/Makefile.features b/cpu/stm32f7/Makefile.features index 2d4e44ccb55c..1549e4ac5def 100644 --- a/cpu/stm32f7/Makefile.features +++ b/cpu/stm32f7/Makefile.features @@ -1,2 +1,5 @@ FEATURES_PROVIDED += periph_hwrng +FEATURES_PROVIDED += periph_flashpage +FEATURES_PROVIDED += periph_flashpage_raw + -include $(RIOTCPU)/stm32_common/Makefile.features diff --git a/cpu/stm32f7/include/cpu_conf.h b/cpu/stm32f7/include/cpu_conf.h index 27c456e67900..dfffcb2397bb 100644 --- a/cpu/stm32f7/include/cpu_conf.h +++ b/cpu/stm32f7/include/cpu_conf.h @@ -43,6 +43,43 @@ extern "C" { #elif defined(CPU_LINE_STM32F722xx) #define CPU_IRQ_NUMOF (104U) #endif +#define CPU_FLASH_BASE FLASH_BASE +/** @} */ + +/** + * @brief Flash sector configuration + * + * @{ + */ +#define FLASH_DUAL_BANK (0) +#if defined(CPU_LINE_STM32F722xx) +#define FLASHSECTOR_SIZE_MIN (16*1024U) +#elif defined(CPU_LINE_STM32F767xx) || defined(CPU_LINE_STM32F769xx) || \ + defined(CPU_LINE_STM32F746xx) +#define FLASHSECTOR_SIZE_MIN (32*1024UL) +#endif + +/** + * @brief Flash page configuration + * + * NOTE: STM32F7 flash is organized in sectors, FLASHPAGE_* is + * defined as a wrapper over sectors. + * + * @{ + */ +/* To keep the same flashpage functionality an arbitrary 1K < FLASHSECTOR_SIZE_MIN + * (size of smaller sector) is defined + */ +/* An erase byte in flash is set to 0xff */ +#define FLASH_ERASE_STATE (0xff) +#define FLASHPAGE_SIZE (1024U) +#define FLASHPAGE_NUMOF (STM32_FLASHSIZE / FLASHPAGE_SIZE) +/* The minimum block size which can be written is 4B. However, the erase + * depends on the specific sector. + */ +#define FLASHPAGE_RAW_BLOCKSIZE (4U) +/* Writing should be always 4 bytes aligned */ +#define FLASHPAGE_RAW_ALIGNMENT (4U) /** @} */ #ifdef __cplusplus