From 8a165c4d71bccd7c9415d599ae97c328d3ee1396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Tue, 24 Nov 2020 16:04:37 +0100 Subject: [PATCH] flash_internal: remove F1 flash utilities, add F4 section utility compared to the STM32F1, the STM32F4 does not used 1 KB flash pages. F4 uses variable large (>= 16 KB) flash sections. this makes using the last page (128 KB instead of 1KB) for EEPROM highly inefficient. caching such large pages before reprogramming small portion is also no doable (there is not enough RAM). thus almost all F1 utilities are not applicable anymore. to help erasing the right section, a utility to get the section from an address is added. --- lib/flash_internal.c | 392 +++++++++---------------------------------- lib/flash_internal.h | 61 +++---- 2 files changed, 105 insertions(+), 348 deletions(-) diff --git a/lib/flash_internal.c b/lib/flash_internal.c index a18b299..37c9fb9 100644 --- a/lib/flash_internal.c +++ b/lib/flash_internal.c @@ -1,4 +1,4 @@ -/** library to read/write internal flash +/** internal flash utilities * @file * @author King Kévin * @copyright SPDX-License-Identifier: GPL-3.0-or-later @@ -11,286 +11,110 @@ /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities -#include // flash utilities #include // device signature definitions +#include // MCU definitions /* own libraries */ -#include "flash_internal.h" // flash storage library API #include "global.h" // global definitions +#include "flash_internal.h" // own definitions -/** flash page size */ -static uint16_t flash_internal_page = 0; -/** end address of flash */ -static uint32_t flash_internal_end = 0; -/** start address of flash memory used for the emulated EEPROM */ -static uint32_t flash_internal_eeprom_start = 0; -/** start address of emulated EEPROM */ -static uint32_t flash_internal_eeprom_address = 0; +/** information about the MCU's flash sections */ +struct flash_sections_info_s { + const uint16_t device_id; /**< the MCU DEV ID */ + const uint8_t number; /**< number of sections */ + const struct flash_internal_section_info_s* sections; /**< size of the sections, in KiB */ +}; -/** find out page size and flash end address */ -static void flash_internal_init(void) +/** information about the STM42F401xB/C flash sections */ +static const struct flash_internal_section_info_s sections_f401xbc[] = { + { + .number = 0, + .size = 16, + .start = FLASH_BASE + (0) * 1024, + .end = FLASH_BASE + (16) * 1024 - 1, + }, + { + .number = 1, + .size = 16, + .start = FLASH_BASE + (0 + 16) * 1024, + .end = FLASH_BASE + (16 + 16) * 1024 - 1, + }, + { + .number = 2, + .size = 16, + .start = FLASH_BASE + (0 + 16 + 16) * 1024, + .end = FLASH_BASE + (16 + 16 + 16) * 1024 - 1, + }, + { + .number = 3, + .size = 16, + .start = FLASH_BASE + (0 + 16 + 16 + 16) * 1024, + .end = FLASH_BASE + (16 + 16 + 16 + 16) * 1024 - 1, + }, + { + .number = 4, + .size = 64, + .start = FLASH_BASE + (0 + 16 + 16 + 16 + 16) * 1024, + .end = FLASH_BASE + (16 + 16 + 16 + 16 + 64) * 1024 - 1, + }, + { + .number = 5, + .size = 128, + .start = FLASH_BASE + (0 + 16 + 16 + 16 + 16 + 64) * 1024, + .end = FLASH_BASE + (16 + 16 + 16 + 16 + 64 + 128) * 1024 - 1, + }, +}; + +static const struct flash_sections_info_s flash_sections_info[] = { + { + .device_id = 0x423, + .number = LENGTH(sections_f401xbc), + .sections = sections_f401xbc, + }, +}; + +bool flash_internal_range(uint32_t address, size_t size) { - if (0 == flash_internal_page) { - flash_internal_page_size(); // get page size - } - if (0 == flash_internal_end) { - if ((uint32_t)&__flash_end >= FLASH_BASE) { - flash_internal_end = (uint32_t)&__flash_end; - } else { - flash_internal_end = FLASH_BASE + DESIG_FLASH_SIZE * 1024; - } - } -} - -/** verify if the data is in the internal flash area - * @param[in] address start address of the data to read - * @param[in] size how much data to read or write, in bytes - * @return if the data is in the internal flash area - */ -static bool flash_internal_range(uint32_t address, size_t size) -{ - if (0 == flash_internal_page || 0 == flash_internal_end) { - flash_internal_init(); - } - if (address > (UINT32_MAX - size)) { // on integer overflow will occur - return false; - } if (address < FLASH_BASE) { // start address is before the start of the internal flash return false; } - if ((address + size) > flash_internal_end) { // end address is after the end of the internal flash + if (address > (UINT32_MAX - size)) { // an integer overflow will occur + return false; + } + if ((address + size) > FLASH_BASE + desig_get_flash_size() * 1024) { // end address is after the end of the internal flash return false; } return true; } -/** get flash page size - * @return flash page size (in bytes) +/** find out in which section is this address + * @param[in] address address to find the section for + * @return section in which this address is (NULL if not in flash, section has not been found, or the sections of this device are unknown) */ -uint16_t flash_internal_page_size(void) +const struct flash_internal_section_info_s* flash_internal_section(uint32_t address) { - if (0 == flash_internal_page) { // we don't know the page size yet - if (DESIG_FLASH_SIZE < 256) { - if ((*(uint32_t*)0x1FFFF000 & 0xFFFE0000) == 0x20000000) { // non-connectivity system memory start detected (MSP address pointing to SRAM - flash_internal_page = 1024; - } else { // connectivity system memory start is at 0x1FFFB000 - flash_internal_page = 2048; - } - } else { - flash_internal_page = 2048; + if (!flash_internal_range(address, 0)) { // verify if this address is in flash + return NULL; + } + + // find sections information for this device + uint8_t flash_sections_info_i; + for (flash_sections_info_i = 0; flash_sections_info_i < LENGTH(flash_sections_info); flash_sections_info_i++) { + if ((DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK) == flash_sections_info[flash_sections_info_i].device_id) { + break; // we found the device's section information } } - - return flash_internal_page; -} - -bool flash_internal_read(uint32_t address, uint8_t *buffer, size_t size) -{ - // sanity checks - if (buffer == NULL || size == 0) { - return false; - } - if (!flash_internal_range(address, size)) { - return false; + if (flash_sections_info_i >= LENGTH(flash_sections_info)) { // we did not find the device's section information + return NULL; } - // copy data byte per byte (a more efficient way would be to copy words, than the remaining bytes) - for (size_t i = 0; i < size; i++) { - buffer[i] = *((uint8_t*)address + i); - } - - return true; -} - -int32_t flash_internal_write(uint32_t address, const uint8_t *buffer, size_t size, bool preserve) -{ - // sanity checks - if (buffer == NULL || size == 0 || size % 2) { - return -1; - } else if (address < FLASH_BASE) { - return -2; - } else if (!flash_internal_range(address, size)) { - return -3; - } - - uint32_t written = 0; // number of bytes written - flash_unlock(); // unlock flash to be able to write it - while (size) { // write page by page until all data has been written - // verify of we need to erase the flash before writing it - uint32_t page_start = address - (address % flash_internal_page); // get start of the current page - bool erase = false; // verify if we need to erase the page - bool identical = true; // verify if we actually need to write data, or if the data to be written is the identical to the one already if flash - for (uint32_t i = 0; i < size && (address + i) < (page_start + flash_internal_page); i += 2) { // verify if any word in this page needs to be programmed - if (*(uint16_t*)(buffer + i) != (*(uint16_t*)(address + i))) { // verify if the data to be written is identical to the one already written - identical = false; - // in theory writing flash is only about flipping (individual) bits from 1 (erase state) to 0 - // in practice the micro-controller will only allow to flip individual bits if the whole half-word is erased (set to 0xffff) - if (*(uint16_t*)(address + i) != 0xffff) { // flash is not erased - erase = true; // we need to erase it for it to be written - break; // no need to check further - } - } - } - if (identical) { // no data needs to be changed - // go to end of page, or size - uint32_t remaining = (page_start + flash_internal_page) - address; - if (remaining > size) { - remaining = size; - } - written += remaining; - buffer += remaining; - address += remaining; - size -= remaining; - } else if (erase && preserve) { // erase before - uint8_t page_data[flash_internal_page]; // a copy of the complete page before the erase it - uint16_t page_i = 0; // index for page data - // copy page before address - for (uint32_t flash = page_start; flash < address && flash < (page_start + flash_internal_page) && page_i 0 && page_i < flash_internal_page) { - page_data[page_i++] = *buffer; - buffer++; - address++; - size--; - } - // copy data after buffer until end of page - while (page_i < flash_internal_page) { - page_data[page_i] = *(uint8_t*)(page_start + page_i); - page_i++; - } - flash_erase_page(page_start); // erase current page - if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong - flash_lock(); // lock back flash to protect it - return -6; - } - for (uint16_t i = 0; i < flash_internal_page; i += 2) { // write whole page - if (*((uint16_t*)(page_data + i)) != 0xffff) { // after an erase the bits are set to one, no need to program them - flash_program_half_word(page_start + i, *((uint16_t*)(page_data + i))); - if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong - flash_lock(); // lock back flash to protect it - return -7; - } - } - if (*((uint16_t*)(page_data + i)) != *((uint16_t*)(page_start + i))) { // verify the programmed data is right - flash_lock(); // lock back flash to protect it - return -8; - } - written += 2; - } - } else { // simply copy data until end of page (or end of data) - if (erase) { - flash_erase_page(page_start); // erase current page - if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong - flash_lock(); // lock back flash to protect it - return -9; - } - } - while (size > 0 && address < (page_start + flash_internal_page)) { - if (*((uint16_t*)(buffer)) != *((uint16_t*)(address)) && *((uint16_t*)(buffer)) != 0xffff) { // only program when data is different and bits need to be set - flash_program_half_word(address, *((uint16_t*)(buffer))); // program the data - if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong - flash_lock(); // lock back flash to protect it - return -10; - } - if (*((uint16_t*)address) != *((uint16_t*)buffer)) { // verify the programmed data is right - flash_lock(); // lock back flash to protect it - return -11; - } - } - written += 2; - buffer += 2; - address += 2; - size -= 2; - } + // find in which section this address is + for (uint8_t i = 0; i < flash_sections_info[flash_sections_info_i].number; i++) { + if (address >= flash_sections_info[flash_sections_info_i].sections[i].start && address <= flash_sections_info[flash_sections_info_i].sections[i].end) { + return &flash_sections_info[flash_sections_info_i].sections[i]; } } - flash_lock(); // lock back flash to protect it - - return written; -} - -/* the EEPROM allocated area is erased at first - * the EEPROM data starts at the end of the allocated memory - * each time it is written, the next data segment is placed before the existing one - * a data segment start with the size, which help detecting the segment since the data can be the same as erased data (0xffff) - */ -void flash_internal_eeprom_setup(uint16_t pages) -{ - if (0 == flash_internal_page || 0 == flash_internal_end) { - flash_internal_init(); // get page size and flash end - } - - flash_internal_eeprom_start = 0; // reset start address - flash_internal_eeprom_address = 0; // reset EEPROM address - if (pages > DESIG_FLASH_SIZE * 1024 / flash_internal_page) { // not enough pages are available - return; - } - flash_internal_eeprom_start = flash_internal_end - flash_internal_page * pages; // set EEPROM start (page aligned) - - // find EEPROM in flash (first non-erased word) - for (flash_internal_eeprom_address = flash_internal_eeprom_start; flash_internal_eeprom_address < flash_internal_end && 0xffff == *(uint16_t*)flash_internal_eeprom_address; flash_internal_eeprom_address += 2); -} - -bool flash_internal_eeprom_read(uint8_t *eeprom, uint16_t size) -{ - // sanity checks - if (NULL == eeprom || 0 == size || 0xffff == size || 0 == flash_internal_eeprom_start || 0 == flash_internal_eeprom_address) { - return false; - } - if (size + 2U > flash_internal_end - flash_internal_eeprom_start) { // not enough space - return false; - } - if (size + 2U > flash_internal_end - flash_internal_eeprom_address) { // EEPROM size is too large - return false; - } - if (size != *(uint16_t*)flash_internal_eeprom_address) { // check if size match - return false; - } - - return flash_internal_read(flash_internal_eeprom_address + 2, eeprom, size); // read data -} - -int32_t flash_internal_eeprom_write(const uint8_t *eeprom, uint16_t size) -{ - // sanity checks - if (NULL == eeprom || 0 == size || 0xffff == size || 0 == flash_internal_eeprom_start || 0 == flash_internal_eeprom_address) { - return -1; - } - if (size + 2U > flash_internal_end - flash_internal_eeprom_start) { // not enough space - return -2; - } - - if (flash_internal_eeprom_start + size + 2U > flash_internal_eeprom_address) { // there is not enough free space - // erase all EEPROM allocated pages - flash_unlock(); // unlock flash to be able to erase it - for (uint32_t page_start = flash_internal_eeprom_start; page_start < flash_internal_end; page_start += flash_internal_page) { - flash_erase_page(page_start); // erase current page - if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong - flash_lock(); // lock back flash to protect it - return -3; - } - } - flash_internal_eeprom_address = flash_internal_end; // put address back as the end - } - flash_internal_eeprom_address -= (size + 2U); // get new start of data segment - if (flash_internal_eeprom_address % 2) { // have segment word aligned - flash_internal_eeprom_address--; - } - if (flash_internal_eeprom_address < flash_internal_eeprom_start) { // just to be sure - return -4; - } - - int32_t rc = flash_internal_write(flash_internal_eeprom_address, (uint8_t*)&size, 2, false); // write size - if (2 != rc) { - return (-10 + rc); - } - rc = flash_internal_write(flash_internal_eeprom_address + 2, eeprom, size, false); // write data - if (size != rc) { - return (-10 + rc); - } - return rc; + return NULL; // we did not find the section } uint32_t flash_internal_probe_read_size(void) @@ -310,55 +134,3 @@ uint32_t flash_internal_probe_read_size(void) return address - 1 - FLASH_BASE; } - -uint32_t flash_internal_probe_write_size(void) -{ - if (0 == DESIG_FLASH_SIZE) { // no flash size advertised - return 0; - } - - // prepare for reading the flash - cm_disable_faults(); // disable all faults, particularly BusFault - SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag - SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them) - // prepare for writing the flash - flash_unlock(); // unlock flash to be able to write it - // try reading and writing the flash, page per page - uint32_t start = FLASH_BASE + DESIG_FLASH_SIZE * 1024; // start with the end of the advertised flash - if ((uint32_t)&__flash_end >= FLASH_BASE) { // use linker flash size if provided - start = (uint32_t)&__flash_end; - } - uint32_t address = start; // address to test - const uint16_t test_data = 0x2342; // the data we will write and read to test page - while (address < 0x1FFFEFFF) { // this is where the system memory starts - // try reading the flash - (void)*(volatile uint32_t*)address; // access address - if (0 != (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs - break; // page not readable - } - // try writing the flash - flash_erase_page(address); // erase current page - if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong - break; - } - flash_program_half_word(address, test_data); // writes test data - if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong - break; - } - if (test_data != *((uint16_t*)address)) { // verify data is written correctly - break; - } - flash_erase_page(address); // erase test data - if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong - break; - } - address += flash_internal_page_size(); // go to next page - } - flash_clear_status_flags(); // clear all flag - flash_lock(); // protect again from writing - SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag - SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault - cm_enable_faults(); // re-enable faults - - return address - start; -} diff --git a/lib/flash_internal.h b/lib/flash_internal.h index 605edd6..652fb09 100644 --- a/lib/flash_internal.h +++ b/lib/flash_internal.h @@ -1,4 +1,4 @@ -/** library to read/write internal flash +/** internal flash utilities * @file * @author King Kévin * @copyright SPDX-License-Identifier: GPL-3.0-or-later @@ -7,48 +7,33 @@ */ #pragma once -/** read data from internal flash - * @param[in] address start address of the data to read - * @param[out] buffer where to store the read data - * @param[in] size how much data to read, in bytes - * @return if read succeeded +/** base address of the flash memory + * @note not sure if there is an STM32F4 with another address, or why this is not defined in libopencm3 */ -bool flash_internal_read(uint32_t address, uint8_t *buffer, size_t size); -/** write data to internal flash - * @param[in] address start address where to write data to - * @param[in] buffer data to be written - * @param[in] size how much data to write, in bytes - * @param[in] preserve keep the rest of the page if data needs to be erased - * @return number of bytes written (including preserved data), or negative in case of error - * @note the page will be erased if needed to write the data to the flash - */ -int32_t flash_internal_write(uint32_t address, const uint8_t *buffer, size_t size, bool preserve); -/** get flash page size - * @return flash page size (in bytes) - */ -uint16_t flash_internal_page_size(void); +#define FLASH_BASE (0x08000000U) -/** setup the emulated EEPROM area - * @param[in] pages number of flash pages to allocate for the emulated EEPROM - * @warn area must be at least 4 bytes larger than the structure to write +/** information about a flash section */ +struct flash_internal_section_info_s { + const uint8_t number; /**< section number */ + const uint8_t size; /**< section size, in KiB */ + const uint32_t start; /**< section start address */ + const uint32_t end; /**< section end address */ +}; + +/** verify if the data is in the internal flash area + * @param[in] address start address of the data to read + * @param[in] size how much data to read or write, in bytes + * @return if the data is in the internal flash area */ -void flash_internal_eeprom_setup(uint16_t pages); -/** read emulated EEPROM area - * @param[out] eeprom where to store the EEPROM data - * @param[in] size size of the EEPROM area (in bytes) +bool flash_internal_range(uint32_t address, size_t size); + +/** find out in which section is this address + * @param[in] address address to find the section for + * @return section in which this address is (NULL if not in flash, section has not been found, or the sections of this device are unknown) */ -bool flash_internal_eeprom_read(uint8_t *eeprom, uint16_t size); -/** write emulated EEPROM area - * @param[in] eeprom EEPROM data to be stored - * @param[in] size size of the EEPROM area (in bytes) - * @return number of bytes written (including preserved data), or negative in case of error - */ -int32_t flash_internal_eeprom_write(const uint8_t *eeprom, uint16_t size); +const struct flash_internal_section_info_s* flash_internal_section(uint32_t address); + /** probe the readable size of the internal flash * @return tested size (in bytes) */ uint32_t flash_internal_probe_read_size(void); -/** probe the additional writable size of the internal flash, after the advertised size (and linker provided) - * @return tested size (in bytes) - */ -uint32_t flash_internal_probe_write_size(void);