Czy istnieje ogólny algorytm wyrównywania zużycia mikrokontrolera EEPROM?

Pracuję nad biblioteką Arduino, która zmaksymalizuje żywotność EEPROM AVR. Pobiera liczbę zmiennych, które chcesz przechowywać, i wykonuje resztę. To jest moja próba, która nie działa we wszystkich przypadkach.

Informacje ogólne

Atmel twierdzi, że każda komórka pamięci jest przystosowana do 100 000 cykli zapisu/kasowania. Zapewniają one również notatkę aplikacyjną , która opisuje, jak wykonać niwelację zużycia. Oto podsumowanie noty aplikacyjnej.

Przez przemienne zapisuje między dwoma adresami pamięci, możemy zwiększyć wymazywanie / zapis do 200 000 cykli. Trzy adresy pamięci dają 300 000 cykli kasowania/zapisu i tak dalej. Aby zautomatyzować ten proces, bufor stanu jest używany do śledzenia miejsca następnego zapisu. Bufor stanu musi również mieć taką samą długość jak bufor parametru, ponieważ również na nim musi być wykonywane wyrównywanie zużycia. Ponieważ nie możemy zapisać indeksu następnego zapisu, zwiększamy odpowiedni indeks w stanie bufor.

Oto przykład.
   <------------------- 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 ]

Aby określić, gdzie powinien nastąpić następny zapis, przyjrzymy się różnicy między elementami. Jeśli poprzedni element + 1 nie jest równy kolejnemu elementowi, to tam powinien nastąpić następny zapis. Na przykład:

   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 ]
                                                ^
                                                | 

Powyższe przykłady pokazują jak przedłużyć żywotność pamięci EEPROM dla jednej zmiennej. Dla wielu zmiennych wyobraź sobie segmentację pamięci EEPROM na wiele segmentów o tej samej strukturze danych, ale mniejszych bufory.

Problem

Mam działający kod do tego, co opisałem powyżej. Mój problem polega na tym, że algorytm nie działa, gdy długość bufora >= 256. Oto co się dzieje

   Buffer length of 256. The last zero is from
   the initial state of the buffer. At every index
   previous element + 1 == next element. No way to
   know where the next write should be.

   <-------------- Status Buffer ------------>
   [  1  |  2  | ... | 253 | 254 | 255 |  0  ]


   A similar problem occurs when the buffer length
   is greater than 256. A lookup for a read will think
   the next element is at the first 0 when it should be at 
   255.
   <-------------------- Status Buffer ------------------>
   [  1  |  2  | ... | 253 | 254 | 255 |  0  |  0  |  0  ]

Pytanie

Jak mogę rozwiązać powyższy problem? Czy istnieje lepszy sposób, aby śledzić, gdzie następny element powinien być napisany?

Author: Peter Mortensen, 2012-05-19

2 answers

Kilka myśli o ogólnym przedłużeniu życia pamięci EEPROM:

  1. Komórki EEPROM są zwykle zapisywane (przez sprzęt) w dwuetapowej operacji: najpierw komórka jest kasowana, czyli ustawiana na wszystkie (0b11111111 = 0xff), a następnie bity do zapisania (w rzeczywistości tylko te, które są 0) są faktycznie zapisywane. Bity mogą być ustawione tylko na 0 przez rzeczywistą operację zapisu. Zmiana bitu z 0 Na 1 wymaga skasowania całej komórki, a następnie ponownego napisania nowego wartość.

  2. Jeśli komórka EEPROM zawiera już tę samą wartość, która ma być do niej zapisana - co może mieć miejsce w przypadku większej lub mniejszej liczby danych do (ponownego)zapisu - nie ma potrzeby zapisywania do komórki, zmniejszając zużycie tej operacji zapisu do 0. Można sprawdzić zawartość komórek, aby zdecydować, czy w ogóle trzeba ją zapisać, zamiast zawsze pisać do niej nową wartość.

  3. Połączenie powyższych prowadzi do podejścia, w którym komórka jest tylko wymazane przed zapisem, jeśli w nowej wartości są jakieś 1 bity, gdzie zapisana wartość ma bit 0 (to znaczy, jeśli StoredValue & NewValue != NewValue). Nie ma potrzeby kasowania komórki, jeśli dla nowej wartości nie są wymagane przejścia 0 - > 1 bitowe (StoredValue & NewValue == NewValue).

  4. AVR dostarcza osobne instrukcje do kasowania i zapisu odpowiednio do komórki EEPROM, aby wspierać wyżej wymienione mechanizmy.

  5. Najgorsza prędkość transferu danych do EEPROM oczywiście spadnie, gdy wykonywanie operacji read-compare-erase-write zamiast operacji erase-write. Ma to jednak potencjał, aby całkowicie pominąć operacje kasowania i zapisu dla niektórych / większości komórek, co może zmniejszyć względną karę prędkości.

Jeśli chodzi o twój obecny problem, pomyśl o powyższych punktach: dlaczego nie użyć pojedynczych bitów do przechowywania następnej pozycji zapisu?

Przykład:

Bufor stanu jest inicjowany na wszystkie:

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

Przed uzyskaniem dostępu do wartości w pamięci EEPROM, znajdź pierwszy 1 bit w buforze stanu. Liczba tego bitu reprezentuje adres bieżącej "głowy" twojego (okrągłego) bufora parametru.

Za każdym razem, gdy rozwijasz bufor parametru, Ustaw następny bit w buforze stanu na 0:

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

Then

Value:      0011 1111 1111 1111

Then

Value:      0001 1111 1111 1111

I tak dalej.

Można to zrobić bez wymazywania całej komórki i w ten sposób" zużyje " tylko jeden bit bufora stanu dla każdej aktualizacji-jeśli dane napisane ma tylko jeden bit 0, zbyt.
Aby np. zamienić zapisaną wartość 0111 na nową wartość 0011 DANE do zapisu powinny być 1011 (data = ( newValue XOR oldValue ) XOR 0xff), pozostawiając wszystkie kawałki nietknięte z wyjątkiem jednego, który chcemy zmienić.

Po wyczerpaniu bufora stanu (Wszystkie 0) jest on usuwany w całości i wszystko zaczyna się od nowa.

Zdecydowanym plusem jest to, że tylko jeden bit statusu musi być zachowany na jednostkę bufora parametru, który zużywa tylko 1/8 pamięci w porównaniu z notatką aplikacji Atmel. poza tym znalezienie następnej lokalizacji zapisu będzie znacznie szybsze, ponieważ tylko 1/8 operacji odczytu w buforze stanu będzie potrzebne. (Edit: nieprawda, ponieważ odczyty EEPROM są niemal zerowe pod względem wydajności, podczas gdy wymagane przesunięcie bitowe może trwać kilkadziesiąt cykli.)

Kolejna uwaga: czy uważasz, że warto używać 256 + jednostek bufora parametrów? Jednostki staną się dość małe, gdy zajmuje się np. 1024 bajtami całkowitej dostępnej pamięci EEPROM na urządzeniu. - A 100000 cykli pomnożonych przez 256 to dość ogromna liczba operacji zapisu i jeśli tak duża liczba wydaje się być wymagana, prawdopodobnie jest coś nie tak w algorytmie lub EEPROM nie powinien być w ogóle używany do tego celu. Alternatywnie, zewnętrzny NVRAM byłby dobrym wyborem w niektórych scenariuszach.

Czas dostępu może być również tutaj aspektem: podczas próby spojrzenia w górę i odczytania elementu o, powiedzmy, rozmiarze 3 bajtów w buforze parametru z buforem stanu 256 bajtów, 256 (+3) operacje odczytu będą potrzebne w najgorszym przypadku-ogromne koszty!

Istnieje bardzo ilustracyjny dokument na temat działania komórek EEPROM, zawierający informacje o tym, jak i dlaczego pogorszenia jakości:]}

STMicroelectronics: "jak projektant może w pełni wykorzystać Serial STMicroelectronics EEPROMs", Nota aplikacyjna AN2014

 14
Author: JimmyB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-05 09:42:46

Sugeruję użycie prostego "brudnego" bitu na elementach danych zamiast liczników. O ile skanowanie w poszukiwaniu ostatniego napisanego elementu nie jest zbyt wolne lub chcesz zrobić coś skomplikowanego, jak śledzenie złych komórek EEPROM, nie ma sensu mieć liczników i katalogów.

Algorytm jest bardzo prosty: Ustaw "brudny" bit w każdym zapisanym elemencie i skanuj go, gdy chcesz przeczytać ostatni element lub napisać nowy. Gdy zabraknie czystych miejsc, albo wymaż wszystkie elementy lub (w przypadku Arduino) po prostu odwróć wartość "brudnego" bitu i zacznij od początku. Napisałem szczegółowe wyjaśnienie TUTAJ , jeśli potrzebujesz.

 2
Author: Dmitry Grigoryev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 16:43:40