ardupilot/libraries/AP_FlashStorage/AP_FlashStorage.h

225 lines
7.4 KiB
C++

/*
Please contribute your ideas! See https://dev.ardupilot.org for details
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
a class to allow for FLASH to be used as a memory backed storage
backend for any HAL. The basic methodology is to use a log based
storage system over two flash sectors. Key design elements:
- erase of sectors only called on init, as erase will lock the flash
and prevent code execution
- write using log based system
- read requires scan of all log elements. This is expected to be called rarely
- assumes flash that erases to 0xFF and where writing can only clear
bits, not set them
- assumes flash sectors are much bigger than storage size. If they
aren't then caller can aggregate multiple sectors. Designed for
128k flash sectors with 16k storage size.
- assumes two flash sectors are available
*/
#pragma once
#include <AP_HAL/AP_HAL.h>
/*
we support 3 different types of flash which have different restrictions
*/
#define AP_FLASHSTORAGE_TYPE_F1 1 // F1 and F3
#define AP_FLASHSTORAGE_TYPE_F4 2 // F4 and F7
#define AP_FLASHSTORAGE_TYPE_H7 3 // H7
#ifndef AP_FLASHSTORAGE_TYPE
#if defined(STM32F1) || defined(STM32F3)
/*
the STM32F1 and STM32F3 can't change individual bits from 1 to 0
unless all bits in the 16 bit word are 1
*/
#define AP_FLASHSTORAGE_TYPE AP_FLASHSTORAGE_TYPE_F1
#elif defined(STM32H7)
/*
STM32H7 can only write in 32 byte chunks, and must only write when all bits are 1
*/
#define AP_FLASHSTORAGE_TYPE AP_FLASHSTORAGE_TYPE_H7
#else
/*
STM32HF4 and STM32H7 can update bits from 1 to 0
*/
#define AP_FLASHSTORAGE_TYPE AP_FLASHSTORAGE_TYPE_F4
#endif
#endif
/*
The StorageManager holds the layout of non-volatile storeage
*/
class AP_FlashStorage {
private:
#if AP_FLASHSTORAGE_TYPE == AP_FLASHSTORAGE_TYPE_H7
// need to write in 32 byte chunks, with 2 byte header
static const uint8_t block_size = 30;
static const uint8_t max_write = block_size;
#else
static const uint8_t block_size = 8;
static const uint8_t max_write = 64;
#endif
static const uint16_t num_blocks = (HAL_STORAGE_SIZE+(block_size-1)) / block_size;
public:
// caller provided function to write to a flash sector
FUNCTOR_TYPEDEF(FlashWrite, bool, uint8_t , uint32_t , const uint8_t *, uint16_t );
// caller provided function to read from a flash sector. Only called on init()
FUNCTOR_TYPEDEF(FlashRead, bool, uint8_t , uint32_t , uint8_t *, uint16_t );
// caller provided function to erase a flash sector. Only called from init()
FUNCTOR_TYPEDEF(FlashErase, bool, uint8_t );
// caller provided function to indicate if erasing is allowed
FUNCTOR_TYPEDEF(FlashEraseOK, bool);
// constructor.
AP_FlashStorage(uint8_t *mem_buffer, // buffer of storage_size bytes
uint32_t flash_sector_size, // size of each flash sector in bytes
FlashWrite flash_write, // function to write to flash
FlashRead flash_read, // function to read from flash
FlashErase flash_erase, // function to erase flash
FlashEraseOK flash_erase_ok); // function to check if erasing allowed
// initialise storage, filling mem_buffer with current contents
bool init(void);
// erase sectors and re-initialise
bool erase(void) WARN_IF_UNUSED {
return erase_all();
}
// re-initialise storage, using current mem_buffer
bool re_initialise(void) WARN_IF_UNUSED;
// switch full sector - should only be called when safe to have CPU
// offline for considerable periods as an erase will be needed
bool switch_full_sector(void) WARN_IF_UNUSED;
// write some data to storage from mem_buffer
bool write(uint16_t offset, uint16_t length) WARN_IF_UNUSED;
// fixed storage size
static const uint16_t storage_size = HAL_STORAGE_SIZE;
private:
uint8_t *mem_buffer;
const uint32_t flash_sector_size;
FlashWrite flash_write;
FlashRead flash_read;
FlashErase flash_erase;
FlashEraseOK flash_erase_ok;
uint8_t current_sector;
uint32_t write_offset;
uint32_t reserved_space;
bool write_error;
// 24 bit signature
#if AP_FLASHSTORAGE_TYPE == AP_FLASHSTORAGE_TYPE_F4
static const uint32_t signature = 0x51685B;
#elif AP_FLASHSTORAGE_TYPE == AP_FLASHSTORAGE_TYPE_F1
static const uint32_t signature = 0x51;
#elif AP_FLASHSTORAGE_TYPE == AP_FLASHSTORAGE_TYPE_H7
static const uint32_t signature = 0x51685B62;
#else
#error "Unknown AP_FLASHSTORAGE_TYPE"
#endif
// sector states, representation depends on storage type
enum SectorState {
SECTOR_STATE_AVAILABLE = 1,
SECTOR_STATE_IN_USE = 2,
SECTOR_STATE_FULL = 3,
SECTOR_STATE_INVALID = 4
};
// header in first word of each sector
struct sector_header {
#if AP_FLASHSTORAGE_TYPE == AP_FLASHSTORAGE_TYPE_F4
uint32_t state1:8;
uint32_t signature1:24;
#elif AP_FLASHSTORAGE_TYPE == AP_FLASHSTORAGE_TYPE_F1
uint32_t state1:32;
uint32_t signature1:16;
#elif AP_FLASHSTORAGE_TYPE == AP_FLASHSTORAGE_TYPE_H7
// needs to be 96 bytes on H7 to support 3 states
uint32_t state1;
uint32_t signature1;
uint32_t pad1[6];
uint32_t state2;
uint32_t signature2;
uint32_t pad2[6];
uint32_t state3;
uint32_t signature3;
uint32_t pad3[6];
#endif
bool signature_ok(void) const;
SectorState get_state() const;
void set_state(SectorState state);
};
enum BlockState {
BLOCK_STATE_AVAILABLE = 0x3,
BLOCK_STATE_WRITING = 0x1,
BLOCK_STATE_VALID = 0x0
};
// header of each block of data
struct block_header {
uint16_t state:2;
uint16_t block_num:11;
uint16_t num_blocks_minus_one:3;
};
// amount of space needed to write full storage
static const uint32_t reserve_size = (storage_size / max_write) * (sizeof(block_header) + max_write) + max_write;
// load data from a sector
bool load_sector(uint8_t sector) WARN_IF_UNUSED;
// erase a sector and write header
bool erase_sector(uint8_t sector, bool mark_available) WARN_IF_UNUSED;
// erase all sectors and reset
bool erase_all() WARN_IF_UNUSED;
// write all of mem_buffer to current sector
bool write_all() WARN_IF_UNUSED;
// return true if all bytes are zero
bool all_zero(uint16_t ofs, uint16_t size) WARN_IF_UNUSED;
// switch to next sector for writing
bool switch_sectors(void) WARN_IF_UNUSED;
// _switch_full_sector is protected by switch_full_sector to avoid
// an infinite recursion problem; switch_full_sectory calls
// write() which can call switch_full_sector. This has been seen
// in practice.
bool protected_switch_full_sector(void) WARN_IF_UNUSED;
bool in_switch_full_sector;
};