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: stm32l1: add flashpage writing support #7712

Closed
wants to merge 7 commits into from
Closed
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
120 changes: 120 additions & 0 deletions cpu/stm32_common/periph/flashpage.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

#include "periph/flashpage.h"

#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F1) || \
defined(CPU_FAM_STM32F3)
void flashpage_write(int page, void *data)
{
assert(page < (int)FLASHPAGE_NUMOF);
Expand Down Expand Up @@ -88,3 +90,121 @@ void flashpage_write(int page, void *data)
while (RCC->CR & RCC_CR_HSIRDY) {}
}
}
#elif defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
/* Flash program erase key1 */
#define PEKEY1 (0x89ABCDEF)

/* Flash program erase key: used with FLASH_PEKEY2 to unlock the write
* access to the FLASH_PECR register and data EEPROM
*/
#define PEKEY2 (0x02030405)

/* Flash program memory key1 */
#define PRGKEY1 (0x8C9DAEBF)

/* Flash program memory key2: used with FLASH_PRGKEY2 to unlock
* the program memory
*/
#define PRGKEY2 (0x13141516)

static void _unlock(void)
{
/* Unlocking the Data memory and FLASH_PECR register access*/
if(FLASH->PECR & FLASH_PECR_PRGLOCK)
{
/* Unlock for erase */
FLASH->PEKEYR = PEKEY1;
FLASH->PEKEYR = PEKEY2;

/* Unlock flash registers */
FLASH->PRGKEYR = PRGKEY1;
FLASH->PRGKEYR = PRGKEY2;
}
}

static void _lock(void)
{
/* Set the PRGLOCK and PELOCK Bit to lock the program memory access */
FLASH->PECR |= FLASH_PECR_PRGLOCK;
FLASH->PECR |= FLASH_PECR_PELOCK;
}

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(((unsigned)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;

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

DEBUG("[flashpage_raw] write: now writing the data\n");
for (size_t i = 0; i < len; i++) {
DEBUG("[flashpage_raw] writing %c to %p\n", (char)data_addr[i], dst);
*dst++ = *data_addr++;
while (FLASH->SR & FLASH_SR_BSY) {}
}
DEBUG("[flashpage_raw] write: done writing data\n");

DEBUG("flashpage_raw] now locking the flash module again\n");
_lock();
}

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

/* Using 32bit addresses since we are attempting fast-word erasing/writing*/
uint32_t *page_addr = flashpage_addr(page);

DEBUG("[flashpage] unlocking the flash module\n");
_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) {}

DEBUG("[flashpage] erase: setting the erase and program bits\n");
/* Set the ERASE bit */
FLASH->PECR |= FLASH_PECR_ERASE;
/* Set PROG bit */
FLASH->PECR |= FLASH_PECR_PROG;

DEBUG("address to erase: %p\n", flashpage_addr(page));
DEBUG("[flashpage] erase: trigger the page erase\n");
/* Write 00000000h to the first word of the program page to erase */
*page_addr = 0x00000000;
/* Wait for it to be finished */
DEBUG("[flashpage] erase: wait as long as device is busy\n");
while (FLASH->SR & FLASH_SR_BSY) {}

/* If the erase operation is completed, disable the ERASE and PROG bits */
DEBUG("[flashpage] erase: resetting the page erase and prog bit\n");
FLASH->PECR &= (uint32_t)(~FLASH_PECR_PROG);
Copy link
Contributor

Choose a reason for hiding this comment

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

hm, are you sure about this one? You also disable it in line 168, and shouldn't it only be done there, after you have (potentially) written to flash?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, a leftover of a test, will remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is working like that, since the program bit apparently only affects when erasing the flash.

FLASH->PECR &= (uint32_t)(~FLASH_PECR_ERASE);

if (data != NULL) {
flashpage_write_raw(page_addr, data, FLASHPAGE_SIZE);
}

DEBUG("flashpage] now locking the flash module again\n");
_lock();
}

#endif /* defined(FLASHPAGE_SIZE) && defined(FLASHPAGE_NUMOF) */
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
19 changes: 19 additions & 0 deletions cpu/stm32l1/include/cpu_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ 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 (4)
/* Writing should be always 4 byte aligned */
#define FLASHPAGE_RAW_ALIGNMENT (4)
#ifdef __cplusplus
}
#endif
Expand Down