flash: Write each block individually

Don't gather "blocks" into flash pages.  Instead, write each "block"
to flash on each flash_write_block() request.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2022-05-16 15:36:35 -04:00 committed by Eric Callahan
parent 7287670002
commit 4d969764f1
2 changed files with 114 additions and 105 deletions

View File

@ -356,12 +356,6 @@ config APPLICATION_START
default 0x8002000 if STM32_APP_START_2000 default 0x8002000 if STM32_APP_START_2000
default 0x8008000 default 0x8008000
config MAX_FLASH_PAGE_SIZE
hex
default 0x400 if MACH_STM32F042
default 0x800 if MACH_STM32F072 || MACH_STM32F103
default 0x400
config BLOCK_SIZE config BLOCK_SIZE
int int
default 64 default 64

View File

@ -1,4 +1,4 @@
// Flash (IAP) functionality for STM32F1 // Flash (IAP) functionality for STM32
// //
// Copyright (C) 2021 Eric Callahan <arksine.code@gmail.com // Copyright (C) 2021 Eric Callahan <arksine.code@gmail.com
// //
@ -10,25 +10,57 @@
#include "flash.h" // flash_write_block #include "flash.h" // flash_write_block
#include "internal.h" // FLASH #include "internal.h" // FLASH
#define STM32F4_MIN_SECTOR_SIZE 16384
#define STM32F4_MAX_SECTOR_SIZE 131072
#if CONFIG_MACH_STM32F4 #if CONFIG_MACH_STM32F4
#define FLASH_KEY1 (0x45670123UL) #define FLASH_KEY1 (0x45670123UL)
#define FLASH_KEY2 (0xCDEF89ABUL) #define FLASH_KEY2 (0xCDEF89ABUL)
// Return the flash sector index for the page at the given address
static uint32_t
stm32f4_sector_index(uint32_t addr)
{
if (addr < 0x08010000)
return (addr - 0x08000000) / (16 * 1024);
else if (addr < 0x08020000)
return 4;
else
return 5 + (addr - 0x08020000) / (128 * 1024);
}
#endif #endif
// Return the flash page size at the given address
static uint32_t static uint32_t
flash_get_page_size(void) flash_get_page_size(uint32_t addr)
{ {
if (CONFIG_MACH_STM32F103) { if (CONFIG_MACH_STM32F4) {
if (addr < 0x08010000)
return 16 * 1024;
else if (addr < 0x08020000)
return 64 * 1024;
else
return 128 * 1024;
} else if (CONFIG_MACH_STM32F042) {
return 1024;
} else if (CONFIG_MACH_STM32F103) {
// Check for a 1K page size on the stm32f103 // Check for a 1K page size on the stm32f103
uint16_t *flash_size = (void*)FLASHSIZE_BASE; uint16_t *flash_size = (void*)FLASHSIZE_BASE;
if (*flash_size < 256) if (*flash_size < 256)
return 0x400; return 1024;
} }
return CONFIG_MAX_FLASH_PAGE_SIZE; return 2 * 1024;
} }
// Check if the data at the given address has been erased (all 0xff)
static int
check_erased(uint32_t addr, uint32_t count)
{
uint32_t *p = (void*)addr, *e = (void*)addr + count / 4;
while (p < e)
if (*p++ != 0xffffffff)
return 0;
return 1;
}
// Wait for flash hardware to report ready
static void static void
wait_flash(void) wait_flash(void)
{ {
@ -36,6 +68,7 @@ wait_flash(void)
; ;
} }
// Issue low-level flash hardware unlock sequence
static void static void
unlock_flash(void) unlock_flash(void)
{ {
@ -47,125 +80,107 @@ unlock_flash(void)
wait_flash(); wait_flash();
} }
// Place low-level flash hardware into a locked state
static void static void
lock_flash(void) lock_flash(void)
{ {
FLASH->CR = FLASH_CR_LOCK; FLASH->CR = FLASH_CR_LOCK;
} }
// Issue a low-level flash hardware erase request for a flash page
static void static void
flash_write_stm32f4xx(uint32_t page_address, uint32_t *data) erase_page(uint32_t page_address)
{ {
#if CONFIG_MACH_STM32F4 #if CONFIG_MACH_STM32F4
uint32_t flash_page_size = flash_get_page_size(); FLASH->CR = (FLASH_CR_PSIZE_1 | FLASH_CR_STRT | FLASH_CR_SER
uint32_t* page = (uint32_t*)(page_address); | ((stm32f4_sector_index(page_address) & 0xF) << 3));
#else
uint8_t need_erase = 0;
uint32_t offset_addr = page_address - CONFIG_FLASH_START;
uint32_t sector_index = offset_addr / STM32F4_MAX_SECTOR_SIZE;
if (sector_index < 1) {
need_erase = ((page_address % STM32F4_MIN_SECTOR_SIZE) == 0);
sector_index = offset_addr / STM32F4_MIN_SECTOR_SIZE;
if (sector_index > 3) {
need_erase &= (sector_index == 4);
sector_index = 4;
}
} else {
need_erase = ((page_address % STM32F4_MAX_SECTOR_SIZE) == 0);
sector_index += 4;
}
// make sure flash is unlocked
unlock_flash();
// Erase page
if (need_erase) {
FLASH->CR = (FLASH_CR_PSIZE_1 | FLASH_CR_STRT | FLASH_CR_SER
| ((sector_index & 0xF) << 3));
wait_flash();
}
// Write page
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
for (int i = 0; i < flash_page_size / 4; i++) {
writel(&page[i], data[i]);
wait_flash();
}
lock_flash();
#endif
}
static void
flash_write_stm32f1xx(uint32_t page_address, uint16_t *data)
{
#if CONFIG_MACH_STM32F0 || CONFIG_MACH_STM32F1
uint32_t flash_page_size = flash_get_page_size();
uint16_t* page = (uint16_t*)(page_address);
// make sure flash is unlocked
unlock_flash();
// Erase page
FLASH->CR = FLASH_CR_PER; FLASH->CR = FLASH_CR_PER;
FLASH->AR = page_address; FLASH->AR = page_address;
FLASH->CR = FLASH_CR_PER | FLASH_CR_STRT; FLASH->CR = FLASH_CR_PER | FLASH_CR_STRT;
#endif
wait_flash(); wait_flash();
}
// Write page // Write out a "block" of data to the low-level flash hardware
FLASH->CR = FLASH_CR_PG; static void
for (int i = 0; i < flash_page_size / 2; i++) { write_block(uint32_t block_address, uint32_t *data)
writew(&page[i], data[i]); {
#if CONFIG_MACH_STM32F4
uint32_t *page = (void*)block_address;
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
for (int i = 0; i < CONFIG_BLOCK_SIZE / 4; i++) {
writel(&page[i], data[i]);
wait_flash();
}
#else
uint16_t *page = (void*)block_address, *data16 = (void*)data;
FLASH->CR = FLASH_CR_PG;
for (int i = 0; i < CONFIG_BLOCK_SIZE / 2; i++) {
writew(&page[i], data16[i]);
wait_flash(); wait_flash();
} }
lock_flash();
#endif #endif
} }
static void static uint32_t write_count;
flash_write_page(uint32_t page_address, void *data)
{
if (CONFIG_MACH_STM32F4) {
flash_write_stm32f4xx(page_address, (uint32_t *)data);
} else {
flash_write_stm32f1xx(page_address, (uint16_t *)data);
}
}
static uint8_t page_buffer[CONFIG_MAX_FLASH_PAGE_SIZE] __aligned(4);
// Page Tracking
static uint32_t last_page_address = 0;
static uint8_t page_pending = 0;
static void
write_page(uint32_t page_address)
{
flash_write_page(page_address, page_buffer);
memset(page_buffer, 0xFF, sizeof(page_buffer));
last_page_address = page_address;
page_pending = 0;
}
// Main block write interface
int int
flash_write_block(uint32_t block_address, uint32_t *data) flash_write_block(uint32_t block_address, uint32_t *data)
{ {
uint32_t flash_page_size = flash_get_page_size(); if (block_address & (CONFIG_BLOCK_SIZE - 1))
uint32_t page_pos = block_address % flash_page_size; // Not a block aligned address
memcpy(&page_buffer[page_pos], (uint8_t *)&data[2], CONFIG_BLOCK_SIZE); return -1;
page_pending = 1; uint32_t flash_page_size = flash_get_page_size(block_address);
if (page_pos + CONFIG_BLOCK_SIZE == flash_page_size) uint32_t page_address = ALIGN_DOWN(block_address, flash_page_size);
write_page(block_address - page_pos);
// Check if erase is needed
int need_erase = 0;
if (page_address == block_address) {
if (check_erased(block_address, flash_page_size)) {
// Page already erased
} else if (memcmp(data, (void*)block_address, CONFIG_BLOCK_SIZE) == 0
&& check_erased(block_address + CONFIG_BLOCK_SIZE
, flash_page_size - CONFIG_BLOCK_SIZE)) {
// Retransmitted request - just ignore
return 0;
} else {
need_erase = 1;
}
} else {
if (!check_erased(block_address, CONFIG_BLOCK_SIZE)) {
if (memcmp(data, (void*)block_address, CONFIG_BLOCK_SIZE) == 0)
// Retransmitted request - just ignore
return 0;
// Block not erased - out of order request?
return -2;
}
}
// make sure flash is unlocked
unlock_flash();
// Erase page
if (need_erase)
erase_page(page_address);
// Write block
write_block(block_address, data);
lock_flash();
if (memcmp(data, (void*)block_address, CONFIG_BLOCK_SIZE) != 0)
// Failed to write to flash?!
return -3;
write_count++;
return 0; return 0;
} }
// Main flash complete notification interface
int int
flash_complete(void) flash_complete(void)
{ {
uint32_t flash_page_size = flash_get_page_size(); return write_count;
if (page_pending) {
write_page(last_page_address + flash_page_size);
}
return ((last_page_address - CONFIG_APPLICATION_START)
/ flash_page_size) + 1;
} }