Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
EEPROMEmulation.cxx
Go to the documentation of this file.
1
34#include "EEPROMEmulation.hxx"
35
36#include <cstring>
37
39
40const uint32_t EEPROMEmulation::MAGIC_DIRTY = 0xaa55aa55;
41const uint32_t EEPROMEmulation::MAGIC_INTACT = 0xaa558001;
42const uint32_t EEPROMEmulation::MAGIC_USED = 0x00000000;
43const uint32_t EEPROMEmulation::MAGIC_ERASED = 0xFFFFFFFF;
44
49EEPROMEmulation::EEPROMEmulation(const char *name, size_t file_size)
50 : EEPROM(name, file_size)
51{
52 /* make sure we have an appropriate sized region of memory for our device */
53 HASSERT(EEPROMEMU_FLASH_SIZE >= (2 * SECTOR_SIZE)); // at least two of them
54 HASSERT((EEPROMEMU_FLASH_SIZE % SECTOR_SIZE) == 0); // and nothing remaining
55 HASSERT(file_size <= (SECTOR_SIZE >> 1)); // single block fit all the data
56 HASSERT(file_size <= (1024 * 64 - 2)); // uint16 indexes, 0xffff reserved
57 HASSERT(BLOCK_SIZE >= 4); // we don't support block sizes less than 4 bytes
58 HASSERT(BLOCK_SIZE <= MAX_BLOCK_SIZE); // this is how big our buffers are.
59 HASSERT((BLOCK_SIZE % 4) == 0); // block size must be on 4 byte boundary
60}
61
65{
66 /* look for an active block that is not used up */
67 for (unsigned i = 0; i < sector_count(); ++i)
68 {
72 {
73 activeSector_ = i;
74 break;
75 }
76 }
77
81 {
82 /* our active block is corrupted, we are starting over */
83 uint32_t data[4] = {MAGIC_DIRTY, 0, 0, 0};
84
87
88 data[0] = MAGIC_INTACT;
90
92 }
93 else
94 {
95 /* look for first data block */
96 for (unsigned block_index = rawBlockCount_ - 1;
97 block_index >= MAGIC_COUNT; --block_index)
98 {
99 if (*block(activeSector_, block_index) != MAGIC_ERASED)
100 {
101 break;
102 }
104 }
105 }
106
107 /* do we shadow_ the data in RAM to speed up reads */
108 if (SHADOW_IN_RAM)
109 {
110 /* copy EEPROM data into shadow_ ram */
111 shadow_ = new uint8_t[file_size()];
112
113 /* prime the shadow_ RAM with the EEPROM data */
114 read(0, shadow_, file_size());
115
116 /* turn on shadowing */
117 shadowInRam_ = true;
118 }
119}
120
128void EEPROMEmulation::write(unsigned int index, const void *buf, size_t len)
129{
130 HASSERT((index + len) <= file_size());
131
132 uint8_t* byte_data = (uint8_t*)buf;
133 uint8_t* shadow_data = (uint8_t*)buf;
134 unsigned shadow_index = index;
135 size_t shadow_len = len;
136
137 while (len)
138 {
139 /* get the least significant address bits */
140 unsigned int lsa = index & (BYTES_PER_BLOCK - 1);
141 if (lsa)
142 {
143 /* head, (unaligned) address */
144 uint8_t data[MAX_BLOCK_SIZE];
145 size_t write_size = len < (BYTES_PER_BLOCK - lsa) ?
146 len : (BYTES_PER_BLOCK - lsa);
147 read_fblock(index / BYTES_PER_BLOCK, data);
148
149 if (memcmp(data + lsa, byte_data, write_size) != 0)
150 {
151 /* at least some data has changed */
152 memcpy(data + lsa, byte_data, write_size);
153 write_fblock(index / BYTES_PER_BLOCK, data);
154 }
155
156 index += write_size;
157 len -= write_size;
158 byte_data += write_size;
159 }
160 else if (len < BYTES_PER_BLOCK)
161 {
162 /* tail, (unaligned) address */
163 uint8_t data[MAX_BLOCK_SIZE];
164 read_fblock(index / BYTES_PER_BLOCK, data);
165
166 if (memcmp(data, byte_data, len) != 0)
167 {
168 /* at least some data has changed */
169 memcpy(data, byte_data, len);
170 write_fblock(index / BYTES_PER_BLOCK, data);
171 }
172
173 len = 0;
174 }
175 else
176 {
177 /* aligned data */
178 uint8_t data[MAX_BLOCK_SIZE];
179 read_fblock(index / BYTES_PER_BLOCK, data);
180
181 if (memcmp(data, byte_data, BYTES_PER_BLOCK) != 0)
182 {
183 /* at least some data has changed */
184 memcpy(data, byte_data, BYTES_PER_BLOCK);
185 write_fblock(index / BYTES_PER_BLOCK, data);
186 }
187
188 index += BYTES_PER_BLOCK;
189 len -= BYTES_PER_BLOCK;
190 byte_data += BYTES_PER_BLOCK;
191 }
192 }
193
194 if (shadowInRam_)
195 {
196 memcpy(shadow_ + shadow_index, shadow_data, shadow_len);
197 }
198
200}
201
206void EEPROMEmulation::write_fblock(unsigned int index, const uint8_t data[])
207{
208 if (availableSlots_)
209 {
210 /* still have room in this sector for at least one more write */
211 uint32_t slot_data[MAX_BLOCK_SIZE / sizeof(uint32_t)];
212 for (unsigned int i = 0; i < BLOCK_SIZE / sizeof(uint32_t); ++i)
213 {
214 slot_data[i] = (index << 16) |
215 (data[(i * 2) + 1] << 8) |
216 (data[(i * 2) + 0] << 0);
217 }
220 }
221 else
222 {
223 /* we need to overflow into the next sector */
224 unsigned new_sector = next_active();
225 uint32_t magic[4] = {MAGIC_DIRTY, 0, 0, 0};
226
227 /* prep the new block */
228 flash_erase(new_sector);
229 flash_program(new_sector, MAGIC_DIRTY_INDEX, magic, BLOCK_SIZE);
230
231 /* reset the available count */
232 unsigned available_slots = slot_count();
233
234 /* move any existing data over */
235 for (unsigned int fblock = 0; fblock < (file_size() / BYTES_PER_BLOCK); ++fblock)
236 {
237 uint32_t slot_data[MAX_BLOCK_SIZE / sizeof(uint32_t)];
238 if (fblock == index) // the new data to be written
239 {
240 for (unsigned int i = 0; i < BLOCK_SIZE / sizeof(uint32_t); ++i)
241 {
242 slot_data[i] = (index << 16) |
243 (data[(i * 2) + 1] << 8) |
244 (data[(i * 2) + 0] << 0);
245 }
246 }
247 else
248 {
249 /* this is old data we need to move over */
250 uint8_t read_data[MAX_BLOCK_SIZE];
251 if (!read_fblock(fblock, read_data))
252 {
253 /* nothing to write, this is the default "erased" value */
254 continue;
255 }
256 for (unsigned int i = 0; i < BLOCK_SIZE / sizeof(uint32_t); ++i)
257 {
258 slot_data[i] = (fblock << 16) |
259 (read_data[(i * 2) + 1] << 8) |
260 (read_data[(i * 2) + 0] << 0);
261 }
262 }
263 /* commit the write */
264 flash_program(new_sector, rawBlockCount_ - available_slots, slot_data, BLOCK_SIZE);
265 --available_slots;
266 }
267 /* finalize the data move and write */
268 magic[0] = MAGIC_INTACT;
269 flash_program(new_sector, MAGIC_INTACT_INDEX, magic, BLOCK_SIZE);
270 magic[0] = MAGIC_USED;
272 activeSector_ = new_sector;
273 availableSlots_ = available_slots;
274 }
275}
276
282void EEPROMEmulation::read(unsigned int offset, void *buf, size_t len)
283{
284 HASSERT((offset + len) <= file_size());
285
286 if (shadowInRam_)
287 {
288 memcpy(buf, shadow_ + offset, len);
289 return;
290 }
291
292 uint8_t *byte_data = (uint8_t *)buf;
293 memset(byte_data, 0xff, len); // default if data not found
294
295 for (unsigned block_index = slot_first();
296 block_index < rawBlockCount_ - availableSlots_;
297 ++block_index)
298 {
299 const uint32_t *address = block(activeSector_, block_index);
300 unsigned slot_offset = (address[0] >> 16) * BYTES_PER_BLOCK;
301 // Check if slot overlaps with desired data.
302 if (offset + len <= slot_offset)
303 {
304 continue;
305 }
306 if (slot_offset + BYTES_PER_BLOCK <= offset)
307 {
308 continue;
309 }
310 // Reads the block
311 uint8_t data[MAX_BLOCK_SIZE];
312 for (unsigned int i = 0; i < BLOCK_SIZE / sizeof(uint32_t); ++i)
313 {
314 data[(i * 2) + 0] = (address[i] >> 0) & 0xFF;
315 data[(i * 2) + 1] = (address[i] >> 8) & 0xFF;
316 }
317 // Copies the right part into the output buffer.
318 unsigned slotofs, bufofs;
319 if (slot_offset < offset)
320 {
321 slotofs = offset - slot_offset;
322 bufofs = 0;
323 }
324 else
325 {
326 slotofs = 0;
327 bufofs = slot_offset - offset;
328 }
329 unsigned copylen = BYTES_PER_BLOCK - slotofs;
330 if (slot_offset + BYTES_PER_BLOCK > offset + len)
331 {
332 HASSERT(copylen >= (slot_offset + BYTES_PER_BLOCK) - (offset + len));
333 copylen -= (slot_offset + BYTES_PER_BLOCK) - (offset + len);
334 }
335 memcpy(byte_data + bufofs, data + slotofs, copylen);
336 }
337}
338
345bool EEPROMEmulation::read_fblock(unsigned int index, uint8_t data[])
346{
347 if (shadowInRam_)
348 {
349 memset(data, 0xff, BYTES_PER_BLOCK);
350 if (memcmp(data, shadow_ + (index * BYTES_PER_BLOCK), BYTES_PER_BLOCK) !=
351 0)
352 {
353 memcpy(data, shadow_ + (index * BYTES_PER_BLOCK), BYTES_PER_BLOCK);
354 return true;
355 }
356 return false;
357 }
358 else
359 {
360 /* default data value if not found */
361 memset(data, 0xFF, BYTES_PER_BLOCK);
362
363 /* look for data */
364 for (unsigned raw_block = slot_last();
365 raw_block >= slot_first();
366 --raw_block)
367 {
368 const uint32_t* address = block(activeSector_, raw_block);
369 if (index == (*address >> 16))
370 {
371 /* found the data */
372 for (unsigned int i = 0; i < BLOCK_SIZE / sizeof(uint32_t); ++i)
373 {
374 data[(i * 2) + 0] = (address[i] >> 0) & 0xFF;
375 data[(i * 2) + 1] = (address[i] >> 8) & 0xFF;
376 }
377 return true;
378 }
379 }
380 }
381
382 return false;
383}
#define EEPROMEMU_FLASH_SIZE
Total FLASH memory size to use for EEPROM Emulation.
uint8_t activeSector_
Index of the active sector.
bool read_fblock(unsigned int index, uint8_t data[])
Read from the EEPROM on a native block boundary.
static const size_t HEADER_BLOCK_COUNT
number of reserved header blocks
virtual const uint32_t * block(unsigned sector, unsigned offset)=0
Computes the pointer to load the data stored in a specific block from.
static const uint32_t MAGIC_ERASED
magic marker for an erased block
virtual void flash_program(unsigned sector, unsigned start_block, uint32_t *data, uint32_t byte_count)=0
Simple hardware abstraction for FLASH program API.
void write(unsigned int offset, const void *buf, size_t len) OVERRIDE
Write to the EEPROM.
static const uint32_t MAGIC_USED
magic marker for a used block
unsigned next_active()
Get the next active sector pointer.
static const bool SHADOW_IN_RAM
Shadow the EEPROM data in RAM.
void write_fblock(unsigned int index, const uint8_t data[])
Write to the EEPROM on a native block boundary.
static const size_t BLOCK_SIZE
block size in bytes
@ MAGIC_DIRTY_INDEX
dirty metadata block index: programmed when we start writing to the sector (i.e.
@ MAGIC_USED_INDEX
used metadata block index: programmed when the data has been copied to a new sector.
@ MAGIC_COUNT
total metadata block count
@ MAGIC_INTACT_INDEX
intact metadata block index: programmed when the data in this sector is authoritative.
static constexpr unsigned MAX_BLOCK_SIZE
Maximum byte size of a single block.
const uint16_t rawBlockCount_
How many blocks are there in a sector.
static const uint32_t MAGIC_DIRTY
magic marker for a block that we are transitioning to intact
EEPROMEmulation()
Default constructor.
bool shadowInRam_
local copy of SHADOW_IN_RAM which we can manipulate at run time.
static const size_t SECTOR_SIZE
Sector size in bytes.
void read(unsigned int offset, void *buf, size_t len) OVERRIDE
Read from the EEPROM.
size_t availableSlots_
Number of available (writable) slots for new data in the active sector.
virtual void flash_erase(unsigned sector)=0
Simple hardware abstraction for FLASH erase API.
void mount()
Mount the EEPROM file.
static const size_t BYTES_PER_BLOCK
useful data bytes size in bytes
static const uint32_t MAGIC_INTACT
magic marker for an intact block
uint8_t * shadow_
pointer to RAM for shadowing EEPROM.
unsigned slot_count()
Total number of EEPROM slots in a FLASH sector.
unsigned sector_count()
Total number of FLASH sectors being used for emulation.
void updated_notification()
This function will be called after every write.
Common base class for all EEPROM access.
Definition EEPROM.hxx:44
size_t file_size()
Get the maximum file size of the EEPROM file.
Definition EEPROM.hxx:81
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138