123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- /*
- VirtualPage.cpp - Flash page management
- Original Copyright (c) 2017 Frank Holtz. All right reserved.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
- #include "VirtualPage.h"
-
- VirtualPageClass VirtualPage;
-
- #ifndef NVM_VIRTUAL_PAGE_SIZE_BITS
- #define NVM_VIRTUAL_PAGE_SIZE_BITS 12
- #elif NVM_VIRTUAL_PAGE_SIZE_BITS < 12
- #error "NVM_VIRTUAL_PAGE_SIZE_BITS must be >= 12"
- #endif
-
- // Calculate virtual page count, when mcuboot is present
- #if defined(MCUBOOT_PRESENT) && !defined(NVM_VIRTUAL_PAGE_COUNT)
- // mcuboot zephyr build via generated_dts_board.h
- #include "generated_dts_board.h"
- // Calculate number of free pages after scratch area
- #define NVM_VIRTUAL_PAGE_COUNT (((CONFIG_FLASH_SIZE_0<<10)-(FLASH_AREA_IMAGE_SCRATCH_OFFSET_0+FLASH_AREA_IMAGE_SCRATCH_SIZE_0)) >> NVM_VIRTUAL_PAGE_SIZE_BITS)
- #endif
-
- // check page size
- #ifndef NVM_VIRTUAL_PAGE_COUNT
- #if FLASH_ERASE_CYCLES >= 20000
- // use 16k of flash memory
- #define NVM_VIRTUAL_PAGE_COUNT 4
- #else
- // use 32k of flash memory
- #define NVM_VIRTUAL_PAGE_COUNT 8
- #endif
- #endif
-
- /*
- * How many virtual pages are skipped from top of flash
- */
- #ifndef NVM_VIRTUAL_PAGE_SKIP_FROM_TOP
- #define NVM_VIRTUAL_PAGE_SKIP_FROM_TOP 0
- #endif
-
- /*
- * Calculate things around NVM_VIRTUAL_PAGE_SIZE
- */
- #define NVM_VIRTUAL_PAGE_SIZE (1 << (NVM_VIRTUAL_PAGE_SIZE_BITS))
- #define NVM_VIRTUAL_PAGE_ADDRESS_MASK (~(NVM_VIRTUAL_PAGE_SIZE - 1))
- #define NVM_VIRTUAL_PAGE_ALIGN(address) \
- { address = (uint32_t *)((uint32_t)address & NVM_VIRTUAL_PAGE_ADDRESS_MASK); }
-
- /*
- * Defines the position of status words in a page.
- * Offsets are defined in words!
- */
- #ifdef FLASH_SUPPORTS_RANDOM_WRITE
- // use first 8 byte for magic, erase counter and status
- #define OFFSET_MAGIC 0
- #define OFFSET_ERASE_COUNTER 1
- #if FLASH_WRITES_PER_WORD > 2
- // use first 12 bytes for magic, erase counter and status
- #define MASK_ERASE_COUNTER 0x00FFFFFF
- #define OFFSET_STATUS_RELEASE_PREPARE 1
- #define OFFSET_STATUS_RELEASE_END 1
- #define METADATA_SIZE 8
- #define OFFSET_DATA 2
- #elif FLASH_WRITES_PER_WORD == 2
- #define MASK_ERASE_COUNTER 0x00FFFFFF
- #define OFFSET_STATUS_RELEASE_PREPARE 2
- #define OFFSET_STATUS_RELEASE_END 2
- #define METADATA_SIZE 12
- #define OFFSET_DATA 3
- #else
- // use first 12 bytes for erase counter, and magic
- #define OFFSET_MAGIC 1
- #define OFFSET_COUNTER 0
- #define MASK_ERASE_COUNTER 0x00FFFFFF
- #define OFFSET_STATUS_RELEASE_PREPARE NVM_VIRTUAL_PAGE_SIZE - 8
- #define OFFSET_STATUS_RELEASE_END NVM_VIRTUAL_PAGE_SIZE - 4
- #define METADATA_SIZE 16
- #define OFFSET_DATA 4
- #endif
-
- #define BIT_STATUS_RELEASE_PREPARE (1 << 30)
- #define BIT_STATUS_RELEASE_END (1 << 31)
-
- #define NVM_VIRTUAL_PAGE_DATA_SIZE (NVM_VIRTUAL_PAGE_SIZE - METADATA_SIZE)
- #else
- // use first 8 byte for magic and erase counter and last 8 byte for page release
- #define OFFSET_MAGIC 1
- #define OFFSET_ERASE_COUNTER 0
- #define OFFSET_DATA 2
- #define OFFSET_STATUS_RELEASE_PREPARE \
- ((NVM_VIRTUAL_PAGE_SIZE - 8) / sizeof(uint32_t))
- #define OFFSET_STATUS_RELEASE_END \
- ((NVM_VIRTUAL_PAGE_SIZE - 4) / sizeof(uint32_t))
-
- #define MASK_ERASE_COUNTER 0xFFFFFFFF
-
- #define BIT_STATUS_RELEASE_PREPARE 1
- #define BIT_STATUS_RELEASE_END 1
-
- #define NVM_VIRTUAL_PAGE_DATA_SIZE (NVM_VIRTUAL_PAGE_SIZE - 16)
-
- #endif
-
- uint16_t VirtualPageClass::size() const
- {
- return (NVM_VIRTUAL_PAGE_DATA_SIZE);
- }
-
- uint16_t VirtualPageClass::length() const
- {
- return (NVM_VIRTUAL_PAGE_DATA_SIZE / 4);
- }
-
- uint16_t VirtualPageClass::page_count() const
- {
- return (NVM_VIRTUAL_PAGE_COUNT - 1);
- }
-
- uint32_t VirtualPageClass::wear_level()
- {
- uint32_t max_erase_cycles = 0;
- for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
- uint32_t erase_cycles = get_page_erase_cycles(get_page_address(i));
- if (erase_cycles > max_erase_cycles) {
- max_erase_cycles = erase_cycles;
- }
- }
- return (uint32_t)((((uint64_t)max_erase_cycles * 10000)) /
- Flash.specified_erase_cycles());
- }
-
- uint32_t *VirtualPageClass::get(uint32_t magic)
- {
-
- // Give back a page prepared for release and not closed
- for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
- uint32_t *page = get_page_address(i);
- if (
- // correct magic is set
- (page[OFFSET_MAGIC] == magic) &&
- // page is in release_prepare mode
- ((page[OFFSET_STATUS_RELEASE_PREPARE] & BIT_STATUS_RELEASE_PREPARE) ==
- 0) &&
- // page is not released
- ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0)) {
- // Return page in release process with priority
- return &page[OFFSET_DATA];
- }
- }
-
- // check if a unreleased page is available
- for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
- uint32_t *page = get_page_address(i);
- if (
- // correct magic is set
- (page[OFFSET_MAGIC] == magic) &&
- // page is not released
- ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0)) {
- // return page in normal operation
- return &page[OFFSET_DATA];
- }
- }
-
- return (uint32_t *)(~0);
- }
-
- uint32_t *VirtualPageClass::allocate(uint32_t magic)
- {
- uint32_t *return_page = (uint32_t *)(~0);
- uint32_t max_erase_cycles = (uint32_t)~0;
-
- // Avoid duplicate allocation of pages, look for the less used page
- for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
- uint32_t *page = get_page_address(i);
-
- // Delete duplicated pages
- if (
- // same magic
- (page[OFFSET_MAGIC] == magic) &&
- // Not in release_end state
- ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0) &&
- // Not in release_prepare state
- (!release_started(page))) {
- // clear the page
- build_page(page, (uint32_t)~0);
- }
-
- uint32_t erase_cycles = get_page_erase_cycles(page);
- // When the page has less erase cycles and is not marked as failed
- if ((erase_cycles < max_erase_cycles) && (page[OFFSET_MAGIC] > 0) &&
- (
- // magic is empty
- (page[OFFSET_MAGIC] == (uint32_t)~0) ||
- // marked as released
- ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) ==
- 0))) {
- max_erase_cycles = erase_cycles;
- return_page = page;
- }
- }
-
- // return if no page was found
- if (return_page == (uint32_t *)~0) {
- return return_page;
- }
-
- build_page(return_page, magic);
- return &return_page[OFFSET_DATA];
- }
-
- uint32_t *VirtualPageClass::allocate(uint32_t magic, uint32_t max_writes)
- {
- // max_writes is not implemented yet -> page is erased with every allocate
- (void)max_writes;
- return allocate(magic);
- }
-
- void VirtualPageClass::release_prepare(uint32_t *address)
- {
- // move pointer to beginning of the page
- NVM_VIRTUAL_PAGE_ALIGN(address);
-
- // Nothing to do at a empty page
- if (address[OFFSET_MAGIC] == (uint32_t)~0) {
- return;
- }
-
- if (release_started(address) == false) {
- // Clear bit BIT_PAGE_RELEASED
- Flash.write(&address[OFFSET_STATUS_RELEASE_PREPARE],
- address[OFFSET_STATUS_RELEASE_PREPARE] &
- ~BIT_STATUS_RELEASE_PREPARE);
- }
- return;
- }
-
- void VirtualPageClass::release(uint32_t *address)
- {
- // move pointer to beginning of the page
- NVM_VIRTUAL_PAGE_ALIGN(address);
-
- // Nothing to do at a empty page
- if (address[OFFSET_MAGIC] == (uint32_t)~0) {
- return;
- }
-
- // Check if status bit already cleared
- if ((address[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0) {
- // Clear bit BIT_PAGE_RELEASED
- Flash.write(&address[OFFSET_STATUS_RELEASE_END],
- address[OFFSET_STATUS_RELEASE_END] & ~BIT_STATUS_RELEASE_END);
- }
- return;
- }
-
- bool VirtualPageClass::release_started(uint32_t *address)
- {
- // move pointer to beginning of the page
- NVM_VIRTUAL_PAGE_ALIGN(address);
-
- return (address[OFFSET_STATUS_RELEASE_PREPARE] &
- BIT_STATUS_RELEASE_PREPARE) == 0;
- }
-
- void VirtualPageClass::fail(uint32_t *address)
- {
- // move pointer to beginning of the page
- NVM_VIRTUAL_PAGE_ALIGN(address);
-
- build_page(address, 0x00000000);
- return;
- }
-
- void VirtualPageClass::clean_up()
- {
- // No page found -> try to give back a page prepared for release
- for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
- uint32_t *page = get_page_address(i);
- if ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) == 0) {
- build_page(get_page_address(i), ~0);
- return; // a maximum of a page is cleaned -> return
- }
- }
- }
-
- void VirtualPageClass::format()
- {
- for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
- uint32_t *address = get_page_address(i);
- build_page(address, (uint32_t)~0);
- }
- }
-
- uint32_t *VirtualPageClass::get_page_address(uint16_t page)
- {
- return (uint32_t *)(Flash.top_app_page_address() -
- ((page + NVM_VIRTUAL_PAGE_SKIP_FROM_TOP)
- << NVM_VIRTUAL_PAGE_SIZE_BITS));
- }
-
- void VirtualPageClass::build_page(uint32_t *address, uint32_t magic)
- {
- // move pointer to beginning of the page
- NVM_VIRTUAL_PAGE_ALIGN(address);
- // get erase counter
- uint32_t erase_counter = get_page_erase_cycles(address);
-
- // Check if a magic is set
- if (address[OFFSET_MAGIC] != (uint32_t)~0) {
- Flash.erase(address, NVM_VIRTUAL_PAGE_SIZE);
- } else {
- // check if page is empty
- for (int i = OFFSET_DATA; i < (NVM_VIRTUAL_PAGE_SIZE / 4); i++) {
- if (address[i] != (uint32_t)~0) {
- Flash.erase(address, NVM_VIRTUAL_PAGE_SIZE);
- break;
- }
- }
- }
-
- // write a new page
- Flash.write(&address[OFFSET_MAGIC], magic);
- if (address[OFFSET_ERASE_COUNTER] == (uint32_t)~0) {
- Flash.write(&address[OFFSET_ERASE_COUNTER],
- erase_counter | ~MASK_ERASE_COUNTER);
- }
- }
-
- uint32_t VirtualPageClass::get_page_erase_cycles(uint32_t *address)
- {
- // Return number of cycles
- return ((((uint32_t)address[OFFSET_ERASE_COUNTER])+1) &
- (uint32_t)MASK_ERASE_COUNTER);
- }
|