удаляем библиотеку

This commit is contained in:
Dmitry Borisenko
2022-12-01 01:27:54 +01:00
parent 9436af94df
commit 2c61580157
295 changed files with 3 additions and 67232 deletions

View File

@@ -1,178 +0,0 @@
/*
* Flash.h - Flash library
* 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
*/
/**
* @file Flash.h
* @brief Flash abstraction layer
*
* @ingroup NVM
* @details Nonvolatile Memory Class
* @{
*/
#pragma once
#include <Arduino.h>
#include <stdio.h> // for size_t
/*
* Define characteristics of Flash
*
* @def FLASH_ERASE_CYCLES
* @brief Specified number of erase cycles
*
* @def FLASH_PAGE_SIZE
* @brief Used/supported Flash page size
*
* @def FLASH_ERASE_PAGE_TIME
* @brief Time in ms to delete a page
*
* @def FLASH_WRITES_PER_WORD
* @brief How often a dataword (32 bit) can be written
*
* @def FLASH_WRITES_PER_PAGE
* @brief How many writes are allowed into a page
*
* @def FLASH_SUPPORTS_RANDOM_WRITE
* @brief Set this if it is allowed to write to a page in random order.
*/
#if defined(NRF51)
#define FLASH_ERASE_CYCLES 20000
#define FLASH_PAGE_SIZE 1024
#define FLASH_ERASE_PAGE_TIME 23
#define FLASH_SUPPORTS_RANDOM_WRITE true
#define FLASH_WRITES_PER_WORD 2
#define FLASH_WRITES_PER_PAGE 512
#elif defined(NRF52)
#define FLASH_ERASE_CYCLES 10000
#define FLASH_PAGE_SIZE 4096
#define FLASH_ERASE_PAGE_TIME 90
#define FLASH_SUPPORTS_RANDOM_WRITE true
#define FLASH_WRITES_PER_WORD 32
#define FLASH_WRITES_PER_PAGE 181
#elif defined(NRF52840)
#define FLASH_ERASE_CYCLES 10000
#define FLASH_PAGE_SIZE 4096
#define FLASH_ERASE_PAGE_TIME 90
#define FLASH_SUPPORTS_RANDOM_WRITE true
#define FLASH_WRITES_PER_WORD 2
#define FLASH_WRITES_PER_PAGE 403
#else
#define FLASH_ERASE_CYCLES 10000 //!< FLASH_ERASE_CYCLES
#define FLASH_PAGE_SIZE 4096 //!< FLASH_PAGE_SIZE
#define FLASH_ERASE_PAGE_TIME 100 //!< FLASH_ERASE_PAGE_TIME
//#define FLASH_SUPPORTS_RANDOM_WRITE true
#define FLASH_WRITES_PER_WORD 1 //!< FLASH_WRITES_PER_WORD
#warning "Unknown platform. Please check the code."
#endif
/**
* @class FlashClass
* @brief This class provides low-level access to internal Flash memory.
*/
class FlashClass
{
public:
//----------------------------------------------------------------------------
/** Constructor */
FlashClass() {};
//----------------------------------------------------------------------------
/** Initialize Flash */
void begin() {};
/** Deinitialize Flash */
void end() {};
//----------------------------------------------------------------------------
/*
* Physical flash geometry
*/
//----------------------------------------------------------------------------
/** Page size in bytes
* @return Number of bytes
*/
uint32_t page_size() const;
//----------------------------------------------------------------------------
/** Page address width in bits. Page size is 2^x
* @return Number of bits
*/
uint8_t page_size_bits() const;
//----------------------------------------------------------------------------
/** Number of managed flash pages
* @return Number of pages
*/
uint32_t page_count() const;
//----------------------------------------------------------------------------
/** Number of page erase cycles
* @return Number of page erase cycles
*/
uint32_t specified_erase_cycles() const;
//----------------------------------------------------------------------------
/** Get a address of a page
* @param[in] page Page number, starting at 0
* @return address of given page
*/
uint32_t *page_address(size_t page);
/** Get top of available flash for application data
* @return Last available address + 1
*/
uint32_t *top_app_page_address();
//----------------------------------------------------------------------------
/*
* Accessing flash memory
*/
//----------------------------------------------------------------------------
/** Erase a page of given size. Size must be page_size aligned!
* Take care about RADIO, WDT and Interrupt timing!
* @param[in] *address Pointer to page
* @param[in] size number of page aligned bytes to erase
*/
void erase(uint32_t *address, size_t size);
//----------------------------------------------------------------------------
/** Erase the complete MCU. This can brick your device!
*/
void erase_all();
//----------------------------------------------------------------------------
/** write a aligned 32 bit word to flash.
* @param[in] *address 32 bit aligned pointer to destination word
* @param[in] value Data word to write
*/
void write(uint32_t *address, uint32_t value);
//----------------------------------------------------------------------------
/** write a aligned block to flash.
* @param[in] *dst_address 32 bit aligned pointer to destination
* @param[in] *src_address 32 bit aligned pointer to source
* @param[in] word_count Number of words to write
*/
void write_block(uint32_t *dst_address, uint32_t *src_address,
uint16_t word_count);
private:
// Wait until flash is ready
void wait_for_ready();
};
extern FlashClass Flash; //!< extern FlashClass
/** Load Hardwarespecific files */
#ifdef NRF5
#include "hal/architecture/NRF5/drivers/Flash.cpp"
#else
#error "Unsupported platform."
#endif
/** @} */

View File

@@ -1,342 +0,0 @@
/*
NVRAM.cpp - Byte wise storage for Virtual Pages.
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 "NVRAM.h"
// VirtualPage magic
#define NVRAM_MAGIC (0x7710fdb9)
// Number of emulated cells
#define NVRAM_LENGTH 3072
// Log configuration: Address index in bits
#define NVRAM_ADDR_POS 20
// Log configuration: Mask for comparsion (4k space)
#define NVRAM_ADDR_MASK 0xfff00000
// Log configuration: Bit position of used address bitmap
#define NVRAM_BITMAP_POS 8
// Log configuration: used address bitmap calulation
#define NVRAM_BITMAP_ADDR_SHIFT 8
// Log configuration: Mask for bitmap extraction
#define NVRAM_BITMAP_MASK 0x000fff00
#define ADDR2BIT(index) \
((1 << (index >> NVRAM_BITMAP_ADDR_SHIFT)) << NVRAM_BITMAP_POS)
NVRAMClass NVRAM;
uint16_t NVRAMClass::length() const
{
return (NVRAM_LENGTH);
}
void NVRAMClass::read_block(uint8_t *dst, uint16_t idx, uint16_t n)
{
uint32_t *vpage;
uint16_t log_start, log_end;
// find correct page
vpage = get_page();
// copy 0xff to dst when no page is available
if (vpage == (uint32_t *)~0) {
for (uint32_t i = 0; i < n; i++) {
((uint8_t *)dst)[i] = 0xff;
}
return;
}
// calculate actual log position
log_end = get_log_position(vpage);
if (log_end == 0) {
log_start = 1;
} else {
log_start = vpage[0] + 1;
}
/*
Serial.print("\r\nread_block idx=");
Serial.print(idx);
Serial.print(" n=");
Serial.print(n);
Serial.print("("); */
while (n > 0) {
// Read cell
*dst = get_byte_from_page(vpage, log_start, log_end, idx);
// Serial.print(*dst, HEX);
// calculate next address
n--;
dst++;
idx++;
}
// Serial.println(")");
}
uint8_t NVRAMClass::read(const uint16_t idx)
{
uint8_t ret;
read_block(&ret, idx, 1);
return ret;
}
bool NVRAMClass::write_block(uint8_t *src, uint16_t idx, uint16_t n)
{
uint32_t *vpage;
uint32_t bitmap;
uint16_t log_start, log_end;
// find correct page
vpage = get_page();
// return on invalid page
if (vpage == (uint32_t *)~0) {
return false;
}
// calculate actual log position
log_start = vpage[0] + 1;
log_end = get_log_position(vpage);
if (log_end > log_start) {
bitmap = vpage[log_end - 1] & NVRAM_BITMAP_MASK;
} else {
bitmap = 0;
}
while (n > 0) {
// Read cell
uint8_t old_value = get_byte_from_page(vpage, log_start, log_end, idx);
uint8_t new_value = *src;
// Have to write into log?
if (new_value != old_value) {
// need to calculate a new page?
if (log_end >= VirtualPage.length()) {
vpage = switch_page(vpage, &log_start, &log_end);
if (vpage == (uint32_t *)~0) {
// do nothing if no page is available
return false;
}
bitmap = 0;
}
// Add Entry into log
Flash.write(&vpage[log_end], (idx << NVRAM_ADDR_POS) | bitmap |
ADDR2BIT(idx) | (uint32_t)new_value);
log_end++;
}
// calculate next address
n--;
src++;
idx++;
}
return true;
}
bool NVRAMClass::write(uint16_t idx, uint8_t value)
{
return (write_block(&value, idx, 1));
}
int NVRAMClass::write_prepare(uint16_t number)
{
// find correct page
uint32_t *vpage = get_page();
// Want to write to much or into an invalid page?
if ((vpage == (uint32_t *)~0) || (number > length())) {
return -1;
}
// calculate actual log position
uint16_t log_end = get_log_position(vpage);
// Calculate number of free bytes in log
int free_bytes = ((VirtualPage.length() - 1) - log_end);
// switch page when
if (free_bytes < number) {
uint16_t log_start = vpage[0] + 1;
vpage = switch_page(vpage, &log_start, &log_end);
if (vpage == (uint32_t *)~0) {
// do nothing if no page is available
return -1;
}
log_end = get_log_position(vpage);
free_bytes = ((VirtualPage.length() - 1) - log_end);
}
return free_bytes;
}
void NVRAMClass::clean_up(uint16_t write_preserve)
{
VirtualPage.clean_up();
if (write_preserve > 0) {
write_prepare(write_preserve);
}
}
uint32_t *NVRAMClass::switch_page(uint32_t *old_vpage, uint16_t *log_start,
uint16_t *log_end)
{
// Mark old page as in release
VirtualPage.release_prepare(old_vpage);
// Get a blank page
uint32_t *new_vpage = VirtualPage.allocate(NVRAM_MAGIC, VirtualPage.length());
if (new_vpage == (uint32_t *)~0) {
// failed
return new_vpage;
}
// Store four bytes for map creation
uint32_t value;
// Length of new map
uint16_t map_length = 0;
// Build map
#ifdef FLASH_SUPPORTS_RANDOM_WRITE
// Copy current values
for (uint16_t i = 0; i < (NVRAM_LENGTH >> 2); i++) {
read_block((uint8_t *)&value, i << 2, 4);
if (value != (uint32_t)~0) {
// Value found
map_length = i + 1;
Flash.write(&new_vpage[i + 1], value);
}
}
// Store map length
Flash.write(new_vpage, map_length);
#else
// find map length
for (uint16_t i = (NVRAM_LENGTH >> 2); i > 0; i--) {
read_block((uint8_t *)&value, i << 2, 4);
if (value != (uint32_t)~0) {
// Value found
map_length = i;
break;
}
}
map_length++;
// Store map length
Flash.write(new_vpage, map_length);
// Copy current values
for (uint16_t i = 0; i <= map_length; i++) {
read_block((uint8_t *)&value, i << 2, 4);
if (value != (uint32_t)~0) {
// Value found
map_length = i;
Flash.write(&new_vpage[i + 1], value);
}
}
#endif
// Release old page
VirtualPage.release(old_vpage);
// Set log position
*log_start = map_length + 1;
*log_end = *log_start;
return new_vpage;
}
uint32_t *NVRAMClass::get_page()
{
uint32_t *vpage = VirtualPage.get(NVRAM_MAGIC);
// Invalid page?
if (vpage == (uint32_t *)~0) {
// Allocate a new page
vpage = VirtualPage.allocate(NVRAM_MAGIC, VirtualPage.length());
// Set map length to 0
Flash.write(&vpage[0], 0x0);
}
return vpage;
}
uint16_t NVRAMClass::get_log_position(uint32_t *vpage)
{
uint16_t position_min = vpage[0] + 1;
uint16_t position_max = VirtualPage.length();
// Return if page is not filled
if ((vpage[0] == (uint32_t)~0) || (position_min >= position_max)) {
return 0;
}
// loop until postition_min != position_max-1
while (position_min != position_max - 1) {
// Calculate middle between min and max
uint16_t mid = position_min + ((position_max - position_min) >> 1);
// Set max or min to current position
if (vpage[mid] == (uint32_t)~0) {
position_max = mid;
} else {
position_min = mid;
}
}
return position_max;
}
uint8_t NVRAMClass::get_byte_from_page(uint32_t *vpage, uint16_t log_start,
uint16_t log_end, uint16_t idx)
{
// mask matching a bit signaling wich address range is in log
uint32_t address_mask = ADDR2BIT(idx);
// mask matching the index address
uint32_t address_match = idx << NVRAM_ADDR_POS;
// Check the log backwards
while (log_end > log_start) {
log_end--;
uint32_t value = vpage[log_end];
// end here if address map bit is not set
if ((value & address_mask) == 0) {
break;
}
// check address match -> update found -> return
if ((value & NVRAM_ADDR_MASK) == address_match) {
return (uint8_t)value;
}
}
// Calculate address in the eeprom map at the beginning of a vpage
uint16_t map_address = (idx >> 2);
map_address++; // jump over log offset field
// look at map if calculated addess before log start position
if (map_address < log_start) {
switch (idx % 4) {
case 3:
return (uint8_t)(vpage[map_address] >> 24);
break;
case 2:
return (uint8_t)(vpage[map_address] >> 16);
break;
case 1:
return (uint8_t)(vpage[map_address] >> 8);
break;
default:
return (uint8_t)(vpage[map_address]);
break;
}
}
// empty cell
return 0xff;
}

View File

@@ -1,113 +0,0 @@
/*
* NVRAM.h - Byte-wise storage for Virtual Pages.
* 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
*/
/**
* @file NVRAM.h
* @brief Byte-wise storage for Virtual Pages.
*
* @defgroup NVM Nonvolatile Memory
* @ingroup internals
* @details Nonvolatile Memory Class
* @{
*/
#pragma once
#include "Flash.h"
#include "VirtualPage.h"
#include <Arduino.h>
/**
* @class NVRAMClass
* @brief Nonvolatile Memory
*/
class NVRAMClass
{
public:
//----------------------------------------------------------------------------
/** Constructor. */
NVRAMClass() {};
//----------------------------------------------------------------------------
/** Initialize Class */
void begin() {};
//----------------------------------------------------------------------------
/** Deinitialize Class */
void end() {};
//----------------------------------------------------------------------------
/** NVM available space in bytes
* @return Number of bytes
*/
uint16_t length() const;
//----------------------------------------------------------------------------
/** Read a block of bytes
* @param[in] *dst Write bytes to given pointer
* @param[in] idx First NVM address to read
* @param[in] n Number of bytes to read
*/
void read_block(uint8_t *dst, uint16_t idx, uint16_t n);
//----------------------------------------------------------------------------
/** Read a byte
* @param[in] idx NVM Address to read
* @return Byte from given address
*/
uint8_t read(const uint16_t idx);
//----------------------------------------------------------------------------
/** Write a block
* @param[in] *src Read bytes from given pointer
* @param[in] idx First NVM address to write
* @param[in] n Number of bytes to write
* @return success
*/
bool write_block(uint8_t *src, uint16_t idx, uint16_t n);
//----------------------------------------------------------------------------
/** Write a byte
* @param[in] idx NVM Address to write
* @param[in] value Byte to write
* @return success
*/
bool write(const uint16_t idx, uint8_t value);
//----------------------------------------------------------------------------
/** Preserve the number of bytes in NVM log for time critical writes. Runtime
* up to 5000ms!
* @param[in] number Bytes to preserve, can 0 to find out free log space
* @return Bytes available for writing
*/
int write_prepare(uint16_t number);
//----------------------------------------------------------------------------
/** lear log if full and prepare released pages for faster reallocation.
* Runtime up to 5000ms!
* @param[in] write_preserve Byte to preserve
*/
void clean_up(uint16_t write_preserve);
private:
// Return a virtual page
uint32_t *get_page();
// Get actual log position
uint16_t get_log_position(uint32_t *vpage);
// Read a byte from page
uint8_t get_byte_from_page(uint32_t *vpage, uint16_t log_start,
uint16_t log_end, uint16_t idx);
// switch a page
uint32_t *switch_page(uint32_t *old_vpage, uint16_t *log_start,
uint16_t *log_end);
};
/** Variable to access the NVRAMClass */
extern NVRAMClass NVRAM;
/** @} */

View File

@@ -1,51 +0,0 @@
This code is based on (arduino-NVM)[https://github.com/d00616/arduino-NVM]. If you change code, please create a Pull Request for arduino-NVM to keep code synchronized.
# arduino-NVM
[![Build Status](https://travis-ci.org/d00616/arduino-NVM.svg?branch=master)](https://travis-ci.org/d00616/arduino-NVM)
This library allows the usage of internal Flash memory. To enhance the limited erase cycles a VirtualPage layer is available. On top of VirtualPage, there is an NVRAM class to allow a lot of writes by using a log-based storage.
For Arduino compatibility, a subset of avr/eeprom.h functionality and a complete port of EEPROM.h is provided.
Accessing bytes via NVRAM or EEPROM is faster than an AVR controller until the internal log is full. At this point, a new page must build. This process takes up to 3400ms (nRF51) or 1300ms (nRF52) depending on your hardware and the highest written address.
To find out more about timing, please run "test_all" example.
_This code is not compatible with any SoftDevice. You have to use the [radio notification](https://devzone.nordicsemi.com/tutorials/14/radio-notification/) and VirtualPage.clean_up()/NVRAM.write_prepare(NUMBER) to ensure that writes are only used in a time without radio activation._
## Flash.h
This class is the hardware abstraction to the Flash controller. Please look into Flash.h for a more detailed description.
Please read the documentation of your microcontroller to find out limitations about writing into flash. You can use the FLASH_... defines in your code to take care about quirks.
## VirtualPage.h
This class provides manages Flash pages. This helps you to reach more erase cycles and handle page faults. The underlying Flash page needs to hold some metadata so a VirtualPage is some bytes smaller than 4k. The size can differ between different hardware.
If you need to allocate VirtualPages in performance critical situations, call VirtualPage.clean_up(), when you have a time slot of more than 100ms.
For VirtualPages the last 16k(nRF51) or 32k(nRF52) are used. This allows the same number of erase cycles on both platforms.
## NVRAM.h
This class provides a 3072 bytes large memory. You can access this memory in a random order without needing to take care of the underlying flash architecture. This class is stateless, this means there is nothing cached in RAM. With every access, the data structure is parsed. This saves RAM and avoids conflicts when you have more than one instance of NVRAM class in your code.
To reach a maximum of write cycles and performance, place all your data at the beginning of the memory. This allows a maximum of write cycles.
When you only use the first 8 Bytes of the NVRAM, you have 5,100,000 write cycles per byte. If you use all 3072 bytes, you have only 3,300 write cycles per byte.
For your own calculation of write cycles, you can calculate the sum of available writes with: (VirtualPage.length()-4-HIGHEST_ADDRESS/4)*40,000
Reading or writing the NVRAM is fast until the internal log is full. On nRF51 you can calculate with 1.2ms and on nRF52 with 0,5ms. If the log is full a new VirtualPage is allocated and written. This procedure can take a time of 3400ms (nRF51) or 1300ms (nRF52).
If you use this code in performance critical situations. Use NVRAM.write_prepare(NUMBER) before to guarantee a fast write for the given number of bytes.
## EEPROM.h and avr_eeprom.h
Both libraries are for Arduino compatibility. Instead of avr/eeprom.h, you have to include avr_eeprom.h. This file maps a limited set of functions to NVRAM.
The EEPROM.h is fully compatible with the AVR version without committing changes.
If you use one of both files, please keep in mind that writing a single byte is fast until the log becomes full. In that case, a single write operation can take up to 3400ms (nRF51) or 1300ms (nRF52).

View File

@@ -1,349 +0,0 @@
/*
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);
}

View File

@@ -1,139 +0,0 @@
/*
VirtualPage.h - 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
*/
/**
* @file VirtualPage.h
* @brief Virtual page management on top of Flash
* The managed pages are organized into VirtualPage.size() sized pages.
* Some Bytes of a Flash page are reserved for a magic number and page management.
*
* @ingroup NVM
* @details Nonvolatile Memory Class
* @{
*/
#pragma once
#include "Flash.h"
#include <Arduino.h>
/**
* @class VirtualPageClass
* @brief Virtual page management on top of Flash
*/
class VirtualPageClass
{
public:
//----------------------------------------------------------------------------
/** Constructor */
VirtualPageClass() {};
//----------------------------------------------------------------------------
/** Initialize Virtual Pages */
void begin() {};
//----------------------------------------------------------------------------
/** Deinitilize Virtual Pages */
void end() {};
//----------------------------------------------------------------------------
/** Reports usable page size in bytes
* @return Number of bytes
*/
uint16_t size() const;
//----------------------------------------------------------------------------
/** Reports usable page size in 32 Bit words
* @return Number of words
*/
uint16_t length() const;
//----------------------------------------------------------------------------
/** Reports the maximum number of allocatable pages
* @return Number of pages
*/
uint16_t page_count() const;
//----------------------------------------------------------------------------
/** Calculates the rate of wear in percent*100.
* Values greater than 10000 indicates exceeding the chip specification.
* This value is only valid for controllers, never erased completely.
* @return calculated level
*/
uint32_t wear_level();
//----------------------------------------------------------------------------
/** Search for a page by given, unique magic number.
* Returns a pointer to (uint32_t *)~0 if there is no page. Don't write
* to this address.
* @param[in] magic A magic number to identify the page.
* @return Pointer to a page
*/
uint32_t *get(uint32_t magic);
//----------------------------------------------------------------------------
/** Returns an address to a blank page or (uint32_t *)~0 if no space
* available. Take care about RADIO, WDT and Interrupt timing! Calculate with
* 0-100ms until a page is available.
* @param[in] magic A magic number to identify the page.
* @return Pointer to a page
*/
uint32_t *allocate(uint32_t magic);
//----------------------------------------------------------------------------
/** Returns an address to a blank page or (uint32_t *)~0 if no space
* available. Take care about RADIO, WDT and Interrupt timing! Calculate with
* 0-100ms until a page is available.
* This function allows using a page multiple times without erasing on some platforms.
* @param[in] magic A magic number to identify the page.
* @param[in] max_writes The number of planned write operations in page lifecycle.
* @return Pointer to a page
*/
uint32_t *allocate(uint32_t magic, uint32_t max_writes);
//----------------------------------------------------------------------------
/** Start releasing a page. You have to allocate a new one and then release the
* old page.
* @param[in] *address A pointer to the page to release
*/
void release_prepare(uint32_t *address);
//----------------------------------------------------------------------------
/** End releasing a page.
* @param[in] *address A pointer to the page to release
*/
void release(uint32_t *address);
//----------------------------------------------------------------------------
/** Returns true if page is in release_prepare state
* @param[in] *address A pointer to a page
* @return Release state
*/
bool release_started(uint32_t *address);
//----------------------------------------------------------------------------
/** Mark a page as a defect page.
*/
void fail(uint32_t *address);
//----------------------------------------------------------------------------
/** Prepare released pages for faster reallocation. Plan with 0-100ms.
*/
void clean_up();
//----------------------------------------------------------------------------
/** Release all pages
*/
void format();
private:
// convert page number 1..max_pages into an address
uint32_t *get_page_address(uint16_t page);
// build a page
void build_page(uint32_t *address, uint32_t magic);
// return number of erase cycles
uint32_t get_page_erase_cycles(uint32_t *address);
};
extern VirtualPageClass VirtualPage; //!< extern VirtualPageClass
/** @} */