W przypadku utraty zasilania podczas kasowania bloku przez układ flash, solidne oprogramowanie powinno zakładać, że zawartość bloku może się dowolnie zmienić w dowolnym momencie , chyba że lub do czasu ponownego usunięcia bloku i cykl kasowania przebiega do końca. Nawet jeśli wydaje się, że blok nadal zawiera stare dane, nie ma gwarancji, że będzie to nadal robić przez dłuższy czas. Nawet jeśli wydaje się, że blok został usunięty, nie ma gwarancji, że zaprogramowane bity nie „pojawią się” spontanicznie. Widziałem kilka procesorów z wewnętrzną pamięcią flash, która zawierała możliwość sprawdzenia, czy bity zostały „naprawdę” wygaszone, czy też zostały „dokładnie” zaprogramowane, ale nigdy nie widziałem takiej funkcjonalności ujawnionej przez zewnętrzne urządzenie flash.
Jeśli ktoś chce mieć możliwość okresowego przechowywania danych do flashowania i zapewniać, że w przypadku awarii zasilania każda aktualizacja zakończy się sukcesem lub wcale, należy mieć co najmniej trzy bloki flash i zdefiniować taki protokół, aby kiedykolwiek blok jest usuwany, można to stwierdzić tylko na podstawie zawartości pozostałych dwóch bloków. Istnieje wiele protokołów do realizacji tego; Zaproponuję tutaj prosty, zakładając, że ilość informacji, które mają być przechowywane, to pełny blok minus jedna programowalna jednostka o minimalnym rozmiarze i dostępne są trzy bloki, które nazwiemy X, Y i Z.
Każdy blok będzie zawierał bity „kontrolne”, które są zarezerwowane do śledzenia ważności / statusu usunięcia; Nazwę te bity x, y i z. Podczas działania system zachowa niezmiennik, że blok, który przechowuje prawidłowe dane, będzie miał pusty bit kontrolny; "poprzedzający" blok (X jest poprzedzony Z) będzie miał zaprogramowany bit sterujący. Bity kontrolne dla pozostałego bloku (tego „następującego” po bloku z poprawnymi danymi) będą nieistotne. Jeśli wszystkie bity kontrolne są puste, nic nigdy nie zostało poprawnie napisane; jeśli wszystkie bity kontrolne są zaprogramowane, coś zostało poważnie uszkodzone.
Aby zapisać nowe dane, usuń blok następujący po tym, który zawiera prawidłowe dane, a następnie zapisz nowe dane w tym bloku. Na koniec, jako ostatni krok, zaprogramuj bit kontrolny tego, co kiedyś było bieżącym blokiem. Dopóki ten bit kontrolny nie zostanie zaprogramowany, nic nie będzie obchodzić zawartości bloku, który właśnie został zaprogramowany. Po zaprogramowaniu tego bitu nic nie będzie obchodzić zawartości bloku następującego po nowym bloku. Pod warunkiem, że system ma wystarczającą ilość dostępnej energii, aby zapewnić, że programowanie tego jednego bitu zakończy się sukcesem lub niepowodzeniem, niezawodne działanie jest zapewnione we wszystkich scenariuszach utraty zasilania.
Załóżmy, że zaprogramowano x, y jest puste, a z to cokolwiek. Ponieważ poprawny blok danych musi mieć własną pustą flagę, a flaga poprzedniego bloku musi być zaprogramowana, X nie może być prawidłowym blokiem (zaprogramowana jest flaga x), a Z nie może być poprawnym blokiem (ponieważ zaprogramowano flagę y). W konsekwencji Y jest jedynym blokiem, który może zawierać prawidłowe dane. Blok X zawiera poprzednią wersję danych, a na Z nie można polegać, aby cokolwiek przechować. Gdy zachodzi potrzeba zapisania nowych danych, kod powinien zaczynać się od skasowania Z (niezależnie od tego, czy jest już puste) i zaprogramowania wszystkich danych, które powinien zawierać. Jeśli w dowolnym momencie tego procesu nastąpi utrata zasilania, stan systemu będzie taki sam, jak przed jego rozpoczęciem (na podstawie flag zakłada się, że zawartość Z jest bez znaczenia, więc jej zawartość w ogóle nie wpływa na stan systemu).
Dopiero po zakończeniu wszystkich zapisów do Z i przechowaniu prawidłowych danych należy zaprogramować flagę y. Po zapisaniu tej flagi Z będzie rozpoznawalny jako blok, który przechowuje poprawne dane, ponieważ jego własna flaga będzie pusta, podczas gdy flaga (y) poprzedniego bloku jest zaprogramowana; fakt, że y jest teraz zaprogramowany, oznacza, że Y nie jest już ważny.
Następnym razem, gdy zachodzi potrzeba przechowywania nowych danych, blok X powinien zostać usunięty i przechowywane w nim dane; zakończenie powinno być wskazane flagą programowania z. Po tym czasie Y powinno zostać skasowane i zapisane tam dane, a zakończenie wskazywane przez flagę programowania x. Istotne jest, aby próby zaprogramowania flag x, y i z albo działały do końca, albo nie przynosiły efektów, ale są to jedyne operacje, które muszą mieć „gwarantowaną atomowość” na poziomie sprzętowym. Wszystkie inne zapisy do pamięci będą dokonywane do bloku, którego zawartość nigdy nie będzie nawet sprawdzana (*), chyba że przebiegną do końca.
(*) System generalnie nie będzie w stanie uniknąć dostępu do nieprawidłowy blok, ale odczytana wartość nie wpłynie na zachowanie systemu.
BTW, jeśli ktoś nie jest pewien możliwości zapewnienia, że zapisy flag przebiegną do końca, istnieją różne podejścia z nadmiarowymi bitami flag, które mogą potencjalnie pomóc, ale niezawodność nie będzie już zapewniona. Załóżmy na przykład, że system traci moc, gdy bit y jest częściowo zaprogramowany, więc czasami będzie czytany jako zaprogramowany, ale czasami jako pusty. Jeśli przy pierwszym uruchomieniu y odczytuje się jako puste, następna aktualizacja usunie Z. Jeśli podczas tego wymazywania system utraci zasilanie i przy następnym uruchomieniu, odczyta y zgodnie z zaprogramowaniem, system przyjmie, że Z jest prawidłowy blok. Gdyby y odczytywał tak, jak zaprogramowano oba razy, wówczas Z byłby prawidłowym blokiem, a następnym usuniętym blokiem byłby X. Gdyby odczytano go jako pusty za każdym razem, Z zostałby poprawnie rozpoznany za drugim razem jako nieprawidłowy blok. Chociaż można próbować ustrzec się przed tymi niebezpieczeństwami, dodając zbędne fragmenty flag, takie podejście niewiele pomaga. Można zaprojektować rzeczy w taki sposób, że byłoby „mało prawdopodobne”, aby częściowo zaprogramowane flagi zachowywały się w kłopotliwy sposób, ale to zasadniczo różni się od gwarancji, że jeśli zapis flag działa niepodzielnie, chip nie może zgłosić żadnych innych częściowo zapisanych danych spowodowałoby jakiekolwiek problemy.