|
Open Model Railroad Network (OpenMRN)
|
Emulates EEPROM in FLASH for the Tiva, LPC17xx and LPC40xx platforms. More...
#include <EEPROMEmulation.hxx>
Protected Types | |
| enum | MagicBlockIndex { MAGIC_FIRST_INDEX = 0 , MAGIC_DIRTY_INDEX = 0 , MAGIC_INTACT_INDEX , MAGIC_USED_INDEX , MAGIC_COUNT } |
| Raw block indexes within a sector to mark the state of the sector. More... | |
Protected Member Functions | |
| EEPROMEmulation (const char *name, size_t file_size) | |
| Constructor. | |
| ~EEPROMEmulation () | |
| Destructor. | |
| void | mount () |
| Mount the EEPROM file. | |
| EEPROMEmulation () | |
| Default constructor. | |
| DISALLOW_COPY_AND_ASSIGN (EEPROMEmulation) | |
Protected Member Functions inherited from EEPROM | |
| EEPROM (const char *name, size_t file_size) | |
| Constructor. | |
| ~EEPROM () | |
| Destructor. | |
| size_t | file_size () |
| Get the maximum file size of the EEPROM file. | |
Protected Member Functions inherited from Node | |
| Node (const char *name) | |
| Constructor. | |
| virtual | ~Node () |
| Destructor. | |
| int | open (File *, const char *, int, int) OVERRIDE |
| Open method. | |
| int | close (File *) OVERRIDE |
| Close method. | |
| virtual int | fstat (File *file, struct stat *stat) override |
| Get the status information of a file or device. | |
Protected Member Functions inherited from FileIO | |
| FileIO (const char *name) | |
| Constructor. | |
| virtual | ~FileIO () |
| Destructor. | |
| virtual int | ioctl (File *file, unsigned long int key, unsigned long data) |
| Request an ioctl transaction. | |
| virtual int | fcntl (File *file, int cmd, unsigned long data) |
| Manipulate a file descriptor. | |
| virtual bool | select (File *file, int mode) |
| Device select method. | |
Protected Attributes | |
| const uint8_t | sectorCount_ {(uint8_t)(EEPROMEMU_FLASH_SIZE / SECTOR_SIZE)} |
| Total number of sectors available. | |
| uint8_t | activeSector_ {0} |
| Index of the active sector. | |
| const uint16_t | rawBlockCount_ {(uint16_t)(SECTOR_SIZE / BLOCK_SIZE)} |
| How many blocks are there in a sector. | |
| size_t | availableSlots_ {0} |
| Number of available (writable) slots for new data in the active sector. | |
| bool | shadowInRam_ {false} |
| local copy of SHADOW_IN_RAM which we can manipulate at run time. | |
| uint8_t * | shadow_ {nullptr} |
| pointer to RAM for shadowing EEPROM. | |
Protected Attributes inherited from Node | |
| OSMutex | lock_ |
| protects internal structures. | |
| mode_t | mode_ |
| File open mode, such as O_NONBLOCK. | |
| unsigned int | references_ |
| number of open references | |
Protected Attributes inherited from FileIO | |
| const char * | name |
| device name | |
Static Protected Attributes | |
| static const size_t | SECTOR_SIZE = (4 * 1024) |
| Sector size in bytes. | |
| static const size_t | BLOCK_SIZE = 16 |
| block size in bytes | |
| static constexpr unsigned | MAX_BLOCK_SIZE = 16 |
| Maximum byte size of a single block. | |
| static const uint32_t | MAGIC_INTACT = 0xaa558001 |
| magic marker for an intact block | |
| static const uint32_t | MAGIC_DIRTY = 0xaa55aa55 |
| magic marker for a block that we are transitioning to intact | |
| static const uint32_t | MAGIC_USED = 0x00000000 |
| magic marker for a used block | |
| static const uint32_t | MAGIC_ERASED = 0xFFFFFFFF |
| magic marker for an erased block | |
Static Protected Attributes inherited from FileIO | |
| static const unsigned int | numOpenFiles = 20 |
| static File | files [] |
| File descriptor pool. | |
| static OSMutex | mutex |
| mutual exclusion for fileio | |
Private Member Functions | |
| void | updated_notification () |
| This function will be called after every write. | |
| void | write (unsigned int offset, const void *buf, size_t len) OVERRIDE |
| Write to the EEPROM. | |
| void | read (unsigned int offset, void *buf, size_t len) OVERRIDE |
| Read from the EEPROM. | |
| void | write_fblock (unsigned int index, const uint8_t data[]) |
| Write to the EEPROM on a native block boundary. | |
| bool | read_fblock (unsigned int index, uint8_t data[]) |
| Read from the EEPROM on a native block boundary. | |
| unsigned | next_active () |
| Get the next active sector pointer. | |
| unsigned | sector_count () |
| Total number of FLASH sectors being used for emulation. | |
| unsigned | slot_count () |
| Total number of EEPROM slots in a FLASH sector. | |
| unsigned | slot_last () |
| unsigned | slot_first () |
| virtual const uint32_t * | block (unsigned sector, unsigned offset)=0 |
| Computes the pointer to load the data stored in a specific block from. | |
| virtual void | flash_erase (unsigned sector)=0 |
| Simple hardware abstraction for FLASH erase API. | |
| virtual void | flash_program (unsigned sector, unsigned start_block, uint32_t *data, uint32_t byte_count)=0 |
| Simple hardware abstraction for FLASH program API. | |
Static Private Attributes | |
| static const size_t | BYTES_PER_BLOCK = 8 |
| useful data bytes size in bytes | |
| static const size_t | HEADER_BLOCK_COUNT = 3 |
| number of reserved header blocks | |
| static const bool | SHADOW_IN_RAM = false |
| Shadow the EEPROM data in RAM. | |
Additional Inherited Members | |
Public Member Functions inherited from EEPROM | |
| EEPROM (const char *name, size_t file_size) | |
| Constructor. | |
| size_t | file_size () |
Public Member Functions inherited from Device | |
| Device (const char *name) | |
| Constructor. | |
| virtual | ~Device () |
| Destructor. | |
Static Public Member Functions inherited from Device | |
| static int | open (struct _reent *reent, const char *path, int flags, int mode) |
| Open a file or device. | |
| static int | close (struct _reent *reent, int fd) |
| Close a file or device. | |
| static int | stat (struct _reent *reent, const char *path, struct stat *stat) |
| Get the status information of a file or device. | |
| static int | select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, long long timeout) |
| POSIX select(). | |
| static void | select_clear () |
| Clears the current thread's select bits. | |
Static Public Member Functions inherited from FileIO | |
| static ssize_t | read (struct _reent *reent, int fd, void *buf, size_t count) |
| Read from a file or device. | |
| static ssize_t | write (struct _reent *reent, int fd, const void *buf, size_t count) |
| Write to a file or device. | |
| static _off_t | lseek (struct _reent *reent, int fd, _off_t offset, int whence) |
| Change the offset index of a file or device. | |
| static int | fstat (struct _reent *reent, int fd, struct stat *stat) |
| Get the status information of a file or device. | |
| static int | ioctl (int fd, unsigned long int key, unsigned long data) |
| Request and ioctl transaction. | |
| static int | fcntl (int fd, int cmd, unsigned long data) |
| Manipulate a file descriptor. | |
| static bool | is_device (int fd) |
| Test if the file descriptor belongs to a device. | |
Static Protected Member Functions inherited from Device | |
| static void | select_insert (SelectInfo *info) |
| Add client to list of clients needing woken. | |
| static void | select_wakeup (SelectInfo *info) |
| Wakeup the list of clients needing woken. | |
| static void | select_wakeup_from_isr (SelectInfo *info, int *woken) |
| Wakeup the list of clients needing woken. | |
Static Protected Member Functions inherited from FileIO | |
| static int | fd_alloc (void) |
| Allocate a free file descriptor. | |
| static void | fd_free (int fd) |
| Free up a file descriptor. | |
| static File * | file_lookup (int fd) |
| Looks up a reference to a File corresponding to a given file descriptor. | |
| static int | fd_lookup (File *file) |
| Looks up a file descriptor corresponding to a given File reference. | |
Emulates EEPROM in FLASH for the Tiva, LPC17xx and LPC40xx platforms.
Applicable in general to any microcontroller with self-writeable flash.
Theory of operation:
The EEPROM area is an area of the MCU Flash reserved for the EEPROMEmulation driver. This area must fall onto flash erase boundaries. The driver will perform a journal of every write into this flash area by writing (offset, data) pairs in append mode. Reads will scan through the journal to find the desired data. When the journal gets full, the data is copied to a second flash area, compacting data by keeping overwrites only once, and then the original area is erased.
Specifics and parameters:
The flash area is specified by the linker symbols __eeprom_start and __eeprom_end. It is the responsibility of the memory map linker script to align these at flash erase boundaries. Inside this area there are independently eraseable sectors. The requirement is that at least two independently eraseable sectors be present, in order to allow copying data from one sector to another without endangering data loss due to power interruption.
The layout of each sector is the following: the sector is split into blocks, where each block can be independently written. Each block will be written only once between two erase operations. The first few blocks are reserved for tracking the state of the sector, the rest of the blocks are used as slots holding data payload. The sectors go through the following states (in order): 1) erased. When the sector is all 0xFF. 2) dirty. The data is being copied over into this sector. 3) intact. This sector contains all the data. 4) used. This sector contains old data and can be reused after erasing.
The layout of a slot is very simple: the first two bytes hold the address. The lower two bytes of each 4-byte hold the data payload.
Parameters:
| SECTOR_SIZE | size of independently erased flash areas. Usually in the range of kilobytes; for example somewhere between 1-16 kbytes. |
| EEPROMEMU_FLASH_SIZE | Automatically detected from the linker symbols. An integer (at least 2) multiple of SECTOR_SIZE. Sectors within the designatedflash are will be used in a round-robin manner to maximize flash endurance. |
| BLOCK_SIZE | Defines how many bytes shall be flashed in one operation. Usually a small integer, at least 4, defined by the hardware limitations of the MCU flash (for example on the NXP 17xx it is 16 bytes, because programming less than 16 bytes in one go is not supported). |
| BYTES_PER_BLOCK | how many bytes of actual data should be stored in a block. Must be <= BLOCK_SIZE - 2 (in order to leave space for the address in the block). Must be a power of two. |
| SHADOW_IN_RAM | a boolean, if set to true, a shadow_ memory are will be allocated in RAM that will be pre-filled with the entire eeprom data. Dramatically speeds up reads, because reads will not have to go through the log anymore. |
| file_size | The total number of bytes held by the emulated eeprom file. Reads from address 0 .. file_size - 1 will be valid. Must be smaller than half of one sector, but should be realistically about 35% of the sector size to avoid too frequent sector erasing. |
Limitations:
At any point in time there is only one active sector. This means that all useful data has to fit in one sector. This limits the max number of bytes to be sector_size / 2. To work around this is makes sense to set the eepromemu sector size to be a multiple of what the microcontroller can erase in one go, and writing the flash_erase in a way that just erases all flash sectors that fall into the chosen eepromemu sector.
Since there is only one active sector, it makes fairly little sense to have more than two sectors in total. One spare sector is needed for copying data over in a power-failure-safe manner. More than two sectors will not extend the size of available eepromemu space, just round-robin with using the flash sectors, thereby extending flash lifetime – which already should not be a problem on modern MCUs.
The efficiency is not great: only 25% of the allocated flash space can be used for data storage. Users should leave some additional buffer to avoid too frequent overflowing of sectors.
The file size is limited to 64k - BLOCK_SIZE because the address is stored on 2 bytes in each block.
Definition at line 130 of file EEPROMEmulation.hxx.
|
protected |
Raw block indexes within a sector to mark the state of the sector.
Definition at line 216 of file EEPROMEmulation.hxx.
|
protected |
Constructor.
| name | device name |
| file_size | maximum file size that we can grow to. |
Definition at line 49 of file EEPROMEmulation.cxx.
|
inlineprotected |
Destructor.
Definition at line 141 of file EEPROMEmulation.hxx.
|
privatepure virtual |
Computes the pointer to load the data stored in a specific block from.
| sector | sector number [0..sectorCount_ - 1] |
| offset | block index within sector, [0..rawBlockCount_ - 1] |
Implemented in MyEEPROM, LpcEEPROMEmulation, Stm32EEPROMEmulation, and TivaEEPROMEmulation.
|
privatepure virtual |
Simple hardware abstraction for FLASH erase API.
| sector | Number of sector [0.. sectorCount_ - 1] to erase |
Implemented in LpcEEPROMEmulation, Stm32EEPROMEmulation, TivaEEPROMEmulation, and MyEEPROM.
|
privatepure virtual |
Simple hardware abstraction for FLASH program API.
| sector | the sector to write to [0..sectorCount_ - 1] |
| start_block | the block index to start writing to [0..rawBlockCount_ - 1] |
| data | a pointer to the data to be programmed |
| byte_count | the number of bytes to be programmed. Must be a multiple of BLOCK_SIZE |
The bytes to program cannot overflow beyond the end of sector, so start_block + byte_count / BLOCK_SIZE <= rawBlockCount_ must hold.
Implemented in MyEEPROM, LpcEEPROMEmulation, Stm32EEPROMEmulation, and TivaEEPROMEmulation.
|
protected |
Mount the EEPROM file.
Should be called during construction of the derived class.
Definition at line 64 of file EEPROMEmulation.cxx.
|
inlineprivate |
Get the next active sector pointer.
Definition at line 246 of file EEPROMEmulation.hxx.
|
privatevirtual |
Read from the EEPROM.
| offset | index within EEPROM address space to start read |
| buf | location to post read data |
| len | length in bytes of data to read |
| offset | within EEPROM address space to start read |
| buf | location to post read data |
| len | length in bytes of data to read |
Implements EEPROM.
Definition at line 282 of file EEPROMEmulation.cxx.
|
private |
Read from the EEPROM on a native block boundary.
| index | block within EEPROM address space to read |
| data | location to place read data, array size must be BYTES_PER_BLOCK large |
| index | block within EEPROM address space to read |
| data | location to place read data, array size must be BYTES_PER_BLOCK large |
| return | true if any of the data is not "erased", else return false |
Definition at line 345 of file EEPROMEmulation.cxx.
|
inlineprivate |
Total number of FLASH sectors being used for emulation.
Definition at line 256 of file EEPROMEmulation.hxx.
|
inlineprivate |
Total number of EEPROM slots in a FLASH sector.
A slot is a block that contains user data (i.e., excludes any metadata blocks).
Definition at line 265 of file EEPROMEmulation.hxx.
|
inlineprivate |
Definition at line 281 of file EEPROMEmulation.hxx.
|
inlineprivate |
Definition at line 273 of file EEPROMEmulation.hxx.
|
private |
This function will be called after every write.
The default implementation is a weak symbol with an empty function. It is intended to be overridden in the application to get callbacks for eeprom writes that can trigger a reload.
Definition at line 45 of file EEPROMEmulation_weak.cxx.
|
privatevirtual |
Write to the EEPROM.
NOTE!!! This is not necessarily atomic across byte boundaries in the case of power loss. The user should take this into account as it relates to data integrity of a whole block.
| offset | index within EEPROM address space to start write |
| buf | data to write |
| len | length in bytes of data to write |
NOTE!!! This is not necessarily atomic across BLOCK_SIZE boundaries in the case of power loss. The user should take this into account as it relates to data integrity of a whole block.
| index | within EEPROM address space to start write |
| buf | data to write |
| len | length in bytes of data to write |
Implements EEPROM.
Definition at line 128 of file EEPROMEmulation.cxx.
|
private |
Write to the EEPROM on a native block boundary.
| index | block within EEPROM address space to write |
| data | data to write, array size must be BYTES_PER_BLOCK large |
Definition at line 206 of file EEPROMEmulation.cxx.
|
protected |
Index of the active sector.
Definition at line 316 of file EEPROMEmulation.hxx.
|
protected |
Number of available (writable) slots for new data in the active sector.
Definition at line 322 of file EEPROMEmulation.hxx.
|
staticprotected |
block size in bytes
Definition at line 154 of file EEPROMEmulation.hxx.
|
staticprivate |
useful data bytes size in bytes
Definition at line 192 of file EEPROMEmulation.hxx.
|
staticprivate |
number of reserved header blocks
Definition at line 195 of file EEPROMEmulation.hxx.
|
staticprotected |
magic marker for a block that we are transitioning to intact
Definition at line 207 of file EEPROMEmulation.hxx.
|
staticprotected |
magic marker for an erased block
Definition at line 213 of file EEPROMEmulation.hxx.
|
staticprotected |
magic marker for an intact block
Definition at line 204 of file EEPROMEmulation.hxx.
|
staticprotected |
magic marker for a used block
Definition at line 210 of file EEPROMEmulation.hxx.
|
staticconstexprprotected |
Maximum byte size of a single block.
Definition at line 157 of file EEPROMEmulation.hxx.
|
protected |
How many blocks are there in a sector.
Definition at line 319 of file EEPROMEmulation.hxx.
|
staticprotected |
Sector size in bytes.
Definition at line 151 of file EEPROMEmulation.hxx.
|
protected |
Total number of sectors available.
Definition at line 313 of file EEPROMEmulation.hxx.
|
protected |
pointer to RAM for shadowing EEPROM.
Definition at line 328 of file EEPROMEmulation.hxx.
|
staticprivate |
Shadow the EEPROM data in RAM.
This will increase read performance at the expense of additional RAM usage.
Definition at line 200 of file EEPROMEmulation.hxx.
|
protected |
local copy of SHADOW_IN_RAM which we can manipulate at run time.
Specifies whether the shadowing is active.
Definition at line 325 of file EEPROMEmulation.hxx.