Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu/stm32: unify support for flashpage/flashpage_raw with L0/L1/F0/F1 families #8768

Merged
merged 5 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 150 additions & 37 deletions cpu/stm32_common/periph/flashpage.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2016 Freie Universität Berlin
* 2018 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
Expand All @@ -15,76 +16,188 @@
* @brief Low-level flash page driver implementation
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Francisco Acosta <francisco.acosta@inria.fr>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/

#include "cpu.h"
#include "stmclk.h"
#include "assert.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

#include "periph/flashpage.h"

void flashpage_write(int page, void *data)
#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
/* Data EEPROM and control register unlock keys */
#define FLASH_KEY1 ((uint32_t)0x89ABCDEF)
#define FLASH_KEY2 ((uint32_t)0x02030405)
/* Program memory unlock keys */
#define FLASH_PRGKEY1 ((uint32_t)0x8C9DAEBF)
#define FLASH_PRGKEY2 ((uint32_t)0x13141516)
#define CNTRL_REG (FLASH->PECR)
#define CNTRL_REG_LOCK (FLASH_PECR_PELOCK)
#define KEY_REG (FLASH->PEKEYR)
#define FLASH_CR_PER (FLASH_PECR_ERASE | FLASH_PECR_PROG)
#define FLASH_CR_PG (FLASH_PECR_FPRG | FLASH_PECR_PROG)
#define FLASHPAGE_DIV (4U) /* write 4 bytes in one go */
#else
#define CNTRL_REG (FLASH->CR)
#define CNTRL_REG_LOCK (FLASH_CR_LOCK)
#define KEY_REG (FLASH->KEYR)
#define FLASHPAGE_DIV (2U)
#endif

static void _unlock(void)
{
assert(page < (int)FLASHPAGE_NUMOF);
DEBUG("[flashpage] unlocking the flash module\n");
if (CNTRL_REG & CNTRL_REG_LOCK) {
KEY_REG = FLASH_KEY1;
KEY_REG = FLASH_KEY2;
}

uint16_t *page_addr = flashpage_addr(page);
uint16_t *data_addr = (uint16_t *)data;
uint32_t hsi_state = (RCC->CR & RCC_CR_HSION);
#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
DEBUG("[flashpage] unlocking the flash program memory\n");
if (!(CNTRL_REG & CNTRL_REG_LOCK)) {
if (CNTRL_REG & FLASH_PECR_PRGLOCK) {
DEBUG("[flashpage] setting the program memory unlock keys\n");
FLASH->PRGKEYR = FLASH_PRGKEY1;
FLASH->PRGKEYR = FLASH_PRGKEY2;
}
}
#endif
}

static void _lock(void)
{
DEBUG("[flashpage] locking the flash module\n");
CNTRL_REG |= CNTRL_REG_LOCK;
}

static void _erase_page(void *page_addr)
{
#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
uint32_t *dst = page_addr;
#else
uint16_t *dst = page_addr;

uint32_t hsi_state = (RCC->CR & RCC_CR_HSION);
/* the internal RC oscillator (HSI) must be enabled */
RCC->CR |= (RCC_CR_HSION);
while (!(RCC->CR & RCC_CR_HSIRDY)) {}
stmclk_enable_hsi();
#endif

/* unlock the flash module */
DEBUG("[flashpage] unlocking the flash module\n");
if (FLASH->CR & FLASH_CR_LOCK) {
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
/* unlock the flash module */
_unlock();

/* ERASE sequence */
/* make sure no flash operation is ongoing */
DEBUG("[flashpage] erase: waiting for any operation to finish\n");
while (FLASH->SR & FLASH_SR_BSY) {}
/* set page erase bit and program page address */
DEBUG("[flashpage] erase: setting the erase bit and page address\n");
FLASH->CR |= FLASH_CR_PER;
FLASH->AR = (uint32_t)flashpage_addr(page);
DEBUG("address to erase: %p\n", flashpage_addr(page));
DEBUG("[flashpage] erase: setting the erase bit\n");
CNTRL_REG |= FLASH_CR_PER;
DEBUG("address to erase: %p\n", page_addr);
#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
DEBUG("[flashpage] erase: trigger the page erase\n");
*dst = (uint32_t)0;
#else
DEBUG("[flashpage] erase: setting the page address\n");
FLASH->AR = (uint32_t)dst;
/* trigger the page erase and wait for it to be finished */
DEBUG("[flashpage] erase: trigger the page erase\n");
FLASH->CR |= FLASH_CR_STRT;
CNTRL_REG |= FLASH_CR_STRT;
#endif
DEBUG("[flashpage] erase: wait as long as device is busy\n");
while (FLASH->SR & FLASH_SR_BSY) {}
/* reset PER bit */
DEBUG("[flashpage] erase: resetting the page erase bit\n");
FLASH->CR &= ~(FLASH_CR_PER);
CNTRL_REG &= ~(FLASH_CR_PER);

/* WRITE sequence */
if (data != NULL) {
DEBUG("[flashpage] write: now writing the data\n");
/* set PG bit and program page to flash */
FLASH->CR |= FLASH_CR_PG;
for (unsigned i = 0; i < (FLASHPAGE_SIZE / 2); i++) {
*page_addr++ = data_addr[i];
while (FLASH->SR & FLASH_SR_BSY) {}
}
/* clear program bit again */
FLASH->CR &= ~(FLASH_CR_PG);
DEBUG("[flashpage] write: done writing data\n");
/* lock the flash module again */
_lock();

#if !(defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1))
/* restore the HSI state */
if (!hsi_state) {
stmclk_disable_hsi();
}
#endif
}

void flashpage_write_raw(void *target_addr, void *data, size_t len)
{
/* assert multiples of FLASHPAGE_RAW_BLOCKSIZE are written and no less of
that length. */
assert(!(len % FLASHPAGE_RAW_BLOCKSIZE));

/* ensure writes are aligned */
assert(!(((unsigned)target_addr % FLASHPAGE_RAW_ALIGNMENT) ||
((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)) + 1);

#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
uint32_t *dst = target_addr;
uint32_t *data_addr = (uint32_t *)data;
#else
uint16_t *dst = (uint16_t *)target_addr;
uint16_t *data_addr = (uint16_t *)data;

uint32_t hsi_state = (RCC->CR & RCC_CR_HSION);
/* the internal RC oscillator (HSI) must be enabled */
stmclk_enable_hsi();
#endif

DEBUG("[flashpage_raw] unlocking the flash module\n");
_unlock();

DEBUG("[flashpage] write: now writing the data\n");
#if !(defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1))
/* 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];
while (FLASH->SR & FLASH_SR_BSY) {}
}

/* clear program bit again */
CNTRL_REG &= ~(FLASH_CR_PG);
DEBUG("[flashpage] write: done writing data\n");

/* finally, lock the flash module again */
DEBUG("flashpage] now locking the flash module again\n");
FLASH->CR |= FLASH_CR_LOCK;
DEBUG("flashpage_raw] now locking the flash module again\n");
_lock();

#if !(defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1))
/* restore the HSI state */
if (!hsi_state) {
RCC->CR &= ~(RCC_CR_HSION);
while (RCC->CR & RCC_CR_HSIRDY) {}
stmclk_disable_hsi();
}
#endif
}

void flashpage_write(int page, void *data)
{
assert(page < (int)FLASHPAGE_NUMOF);

#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
/* STM32L0/L1 only supports word sizes */
uint32_t *page_addr = flashpage_addr(page);
#else
/* Default is to support half-word sizes */
uint16_t *page_addr = flashpage_addr(page);
#endif

/* ERASE sequence */
_erase_page(page_addr);

/* WRITE sequence */
if (data != NULL) {
flashpage_write_raw(page_addr, data, FLASHPAGE_SIZE);
}
}
1 change: 1 addition & 0 deletions cpu/stm32f0/Makefile.features
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
ifeq (,$(filter nucleo32-f031,$(BOARD)))
FEATURES_PROVIDED += periph_flashpage
FEATURES_PROVIDED += periph_flashpage_raw
endif

-include $(RIOTCPU)/stm32_common/Makefile.features
7 changes: 7 additions & 0 deletions cpu/stm32f0/include/cpu_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ extern "C" {
#elif defined(CPU_MODEL_STM32F042K6)
#define FLASHPAGE_NUMOF (32U)
#endif

/* The minimum block size which can be written is 2B. However, the erase
* block is always FLASHPAGE_SIZE.
*/
#define FLASHPAGE_RAW_BLOCKSIZE (2U)
/* Writing should be always 4 bytes aligned */
#define FLASHPAGE_RAW_ALIGNMENT (4U)
/** @} */

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions cpu/stm32f1/Makefile.features
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
FEATURES_PROVIDED += periph_flashpage
FEATURES_PROVIDED += periph_flashpage_raw

-include $(RIOTCPU)/stm32_common/Makefile.features
7 changes: 7 additions & 0 deletions cpu/stm32f1/include/cpu_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ extern "C" {
#elif defined(CPU_MODEL_STM32F103RE) || defined(CPU_MODEL_STM32F103ZE)
#define FLASHPAGE_NUMOF (256U)
#endif

/* The minimum block size which can be written is 2B. However, the erase
* block is always FLASHPAGE_SIZE.
*/
#define FLASHPAGE_RAW_BLOCKSIZE (2U)
/* Writing should be always 4 bytes aligned */
#define FLASHPAGE_RAW_ALIGNMENT (4U)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cladmi, It could be one of those 2 lines, can you test with a value of 2 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With ENABLE_DEBUG (1) in stm32_common/periph/flashpage.c it works so it must be something else.

/** @} */

#ifdef __cplusplus
Expand Down
2 changes: 2 additions & 0 deletions cpu/stm32l0/Makefile.features
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
FEATURES_PROVIDED += periph_flashpage
FEATURES_PROVIDED += periph_flashpage_raw
FEATURES_PROVIDED += periph_hwrng

BOARDS_WITHOUT_HWRNG += nucleo32-l031
Expand Down
25 changes: 25 additions & 0 deletions cpu/stm32l0/include/cpu_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,31 @@ extern "C" {
#endif
/** @} */

/**
* @name Flash page configuration
* @{
*/
#if defined(CPU_MODEL_STM32L073RZ) || defined(CPU_MODEL_STM32L072CZ) || \
defined(CPU_MODEL_STM32L053R8) || defined(CPU_MODEL_STM32L031K6)
#define FLASHPAGE_SIZE (128U)
#endif

#if defined(CPU_MODEL_STM32L073RZ) || defined(CPU_MODEL_STM32L072CZ)
#define FLASHPAGE_NUMOF (1536U)
#elif defined(CPU_MODEL_STM32L053R8)
#define FLASHPAGE_NUMOF (512U)
#elif defined(CPU_MODEL_STM32L031K6)
#define FLASHPAGE_NUMOF (256U)
#endif

/* The minimum block size which can be written is 4B. However, the erase
* block is always FLASHPAGE_SIZE.
*/
#define FLASHPAGE_RAW_BLOCKSIZE (4U)
/* Writing should be always 4 byte aligned */
#define FLASHPAGE_RAW_ALIGNMENT (4U)
/** @} */

#ifdef __cplusplus
}
#endif
Expand Down
3 changes: 3 additions & 0 deletions cpu/stm32l1/Makefile.features
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
FEATURES_PROVIDED += periph_flashpage
FEATURES_PROVIDED += periph_flashpage_raw

-include $(RIOTCPU)/stm32_common/Makefile.features
22 changes: 21 additions & 1 deletion cpu/stm32l1/include/cpu_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,30 @@ extern "C" {
#define CPU_FLASH_BASE FLASH_BASE
/** @} */

/**
* @name Flash page configuration
* @{
*/
#if defined(CPU_MODEL_STM32L152RE) || defined(CPU_MODEL_STM32L151RC)
#define FLASHPAGE_SIZE (256U)
#if defined(CPU_MODEL_STM32L152RE)
#define FLASHPAGE_NUMOF (2048U) /* 512KB */
#endif
#if defined(CPU_MODEL_STM32L151RC)
#define FLASHPAGE_NUMOF (1024U) /* 256KB */
#endif
#endif
/* The minimum block size which can be written is 4B. However, the erase
* block is always FLASHPAGE_SIZE.
*/
#define FLASHPAGE_RAW_BLOCKSIZE (4U)
/* Writing should be always 4 bytes aligned */
#define FLASHPAGE_RAW_ALIGNMENT (4U)
/** @} */

#ifdef __cplusplus
}
#endif

#endif /* CPU_CONF_H */
/** @} */
/** @} */