Существует ли общий алгоритм выравнивания износа микроконтроллера EEPROM?

Я работаю над библиотекой Arduino, которая максимизирует срок службы EEPROM AVR. Он принимает количество переменных, которые вы хотите сохранить, и делает все остальное. Это моя попытка, которая срабатывает не во всех случаях.

Справочная информация

Atmel говорит, что каждая ячейка памяти рассчитана на 100 000 циклов записи/стирания. Они также обеспечивают примечание по применению, который описывает, как выполнить выравнивание износа. Вот краткое изложение примечания к приложению.

чередуя записи по двум адресам памяти, мы можем увеличить стирание / запись до 200 000 циклов. Три адреса памяти дает вам 300,000 циклов стирания/записи и так далее. Для автоматизации этого процесса используется буфер состояния для отслеживания того, где должна быть следующая запись. Буфер состояния также должен иметь ту же длину, что и буфер параметров, поскольку на нем также должно выполняться выравнивание износа. Поскольку мы не можем сохранить индекс следующей записи, мы увеличиваем соответствующий индекс в буфере состояния.

вот пример.

   <------------------- EEPROM -------------------->  
   0                                               N
   -------------------------------------------------
       Parameter Buffer    |     Status Buffer     |
   -------------------------------------------------

   Initial state.
   [ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 ]

   First write is a 7. The corresponding position
   in the status buffer is changed to previous value + 1.
   Both buffers are circular.
   [ 7 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 ]

   A second value, 4, is written to the parameter buffer 
   and the second element in the status buffer becomes
   the previous element, 1 in this case, plus 1.
   [ 7 | 4 | 0 | 0 | 0 | 0 | 1 | 2 | 0 | 0 | 0 | 0 ]

   And so on
   [ 7 | 4 | 9 | 0 | 0 | 0 | 1 | 2 | 3 | 0 | 0 | 0 ]

чтобы определить, где должна произойти следующая запись, мы рассмотрим разницу между элементами. Если предыдущий элемент + 1 не равен следующему элементу, то именно здесь должна произойти следующая запись. Например:

   Compute the differences by starting at the first
   element in the status buffer and wrapping around. 
   General algo: previous element + 1 = current element
   1st element:  0 + 1 = 1 = 1st element (move on)
   2nd element:  1 + 1 = 2 = 2nd element (move on)
   3rd element:  2 + 1 = 3 = 3rd element (move on)
   4th element:  3 + 1 = 4 != 4th element (next write occurs here)

   [ 7 | 4 | 9 | 0 | 0 | 0 | 1 | 2 | 3 | 0 | 0 | 0 ]
                 ^                       ^
                 |                       |

   Another edge case to consider is when the incrementing values
   wrap around at 256 because we are writing bytes. With the
   following buffer we know the next write is at the 3rd element 
   because 255 + 1 = 0 != 250 assuming we are using byte arithmetic.

   [ x | x | x | x | x | x | 254 | 255 | 250 | 251 | 252 | 253 ]
                                          ^
                                          | 

   After we write at element 3 the status buffer is incremented 
   to the next value using byte arithmetic and looks like this.
   255 + 1 = 0 (wrap around)
   0 + 1 != 251 (next write is here)

   [ x | x | x | x | x | x | 254 | 255 |  0  | 251 | 252 | 253 ]
                                                ^
                                                | 

эти примеры выше показывают, как продлить срок службы EEPROM для одной переменной. Для нескольких переменных представьте себе сегментацию EEPROM на несколько сегментов с помощью та же структура данных, но меньшие буферы.

2 ответов


некоторые мысли об общем продлении срока службы EEPROM:

  1. ячейки EEPROM обычно записываются (аппаратным обеспечением) в двухэтапной операции: сначала ячейка стирается, то есть устанавливается во все (0b11111111 = 0xff), затем записываются биты (фактически только те, которые равны 0). Бит может быть установлен в 0 только фактической операцией записи. Изменение бита от 0 до 1 требует стирания всей ячейки, а затем перезаписи новой значение.

  2. если ячейка EEPROM уже содержит то же значение, которое должно быть записано в нее - что может иметь место для более или менее данных, которые будут (повторно)записаны-нет необходимости записывать в ячейку вообще, уменьшая износ для этой операции записи до 0. Может потребоваться проверить содержимое ячеек, чтобы решить, нужно ли его вообще писать, а не всегда писать новое значение.

  3. сочетание из вышеуказанное водит к подход, где ячейка стирается только перед записью, если в новом значении есть 1 бит, где сохраненное значение имеет 0 бит (то есть, если StoredValue & NewValue != NewValue). Нет необходимости стирать ячейку, если для нового значения (StoredValue & NewValue == NewValue).

  4. AVR предоставляет отдельные инструкции для стирания и записи, соответственно, в ячейку EEPROM для поддержки упомянутых выше механизмов.

  5. в худшем случае скорость передача данных в EEPROM, конечно, упадет при выполнении операции чтения-сравнения-стирания-записи вместо операции стирания-записи. Однако это может полностью пропустить операции стирания-записи для некоторых / большинства ячеек, которые могут уменьшить относительную скорость штрафа.

для вашей нынешней проблемы подумайте о приведенных выше пунктах: почему бы не использовать одиночные биты для хранения следующей позиции записи?

пример:

буфер состояния инициализировано для всех:

Bit number: 0123 4567 89AB CDEF
Value:      1111 1111 1111 1111

перед доступом к значению в EEPROM найдите первый 1 бит в буфере состояния. Номер этого бита представляет собой адрес текущей "головки" вашего (кругового) буфера параметров.

каждый раз, когда вы продвигаете буфер параметров, установите следующий бит в буфере состояния в 0:

Bit number: 0123 4567 89AB CDEF
Value:      0111 1111 1111 1111

затем

Value:      0011 1111 1111 1111

затем

Value:      0001 1111 1111 1111

и так далее.

это можно сделать без стирание всей ячейки и, таким образом, будет "носить" только один бит буфера состояния для каждого обновления - если записанные данные имеют только один бит 0.
Чтобы включить, например, сохраненное значение 0111 новое значение 0011 данные для записи должны быть 1011 (data = ( newValue XOR oldValue ) XOR 0xff), оставляя все биты нетронутыми, за исключением одного, который мы действительно хотим изменить.

как только буфер состояния исчерпан (все 0), он стирается полностью, и все начинается снова.

определенный плюс здесь в том, что только один бит состояния должен поддерживаться на единицу буфера параметров, который потребляет только 1/8 памяти по сравнению с примечанием приложения Atmel. кроме того, поиск следующего местоположения записи также будет намного быстрее, так как потребуется только 1/8 операций чтения в буфере состояния. (Edit: неверно, потому что EEPROM читает почти нулевую стоимость производительности, в то время как необходимый бит-сдвиг может возьмите несколько десятков циклов.)

еще одно примечание: как вы думаете, на самом деле полезно использовать 256+ буферных единиц параметров? Блоки стали бы довольно малы общаясь с, например, 1024 байтами полного доступного EEPROM на приборе. - И 100000 циклов, умноженных на 256, - это довольно большое количество операций записи, и если это большое число кажется необходимым, вероятно, что-то не так в алгоритме или EEPROM не следует использовать для этой цели вообще. Как альтернатива, внешняя NVRAM будет хорошим выбором в некоторых случаях.

время доступа может быть аспектом и здесь: при попытке найти и прочитать элемент, скажем, размером 3 байта в буфере параметров с буфером состояния 256 байт,256 (+3) операции чтения будут необходимы в худшем случае - огромные издержки!

существует очень иллюстративный документ о работе ячеек EEPROM, в котором описывается, как и почему ухудшение:

STMicroelectronics: "как дизайнер может сделать большую часть серии STMicroelectronics EEPROMs", application note AN2014


Я предлагаю вам использовать простой "грязный" бит на ваших элементах данных вместо счетчиков. Если сканирование последнего записанного элемента не слишком медленное или вы хотите сделать что-то сложное, например отслеживать плохие ячейки EEPROM, нет смысла иметь счетчики и каталоги.

алгоритм очень прост: установите "грязный" бит в каждом элементе, который вы пишете, и сканируйте этот бит, когда вам нужно прочитать последний элемент или написать новый. Когда вы бежите из чистых пятен, или стираете все элементы или (в случае Arduino) просто переверните значение "грязного" бита и начните с самого начала. Я написал подробное объяснение здесь, если вам нужно.