Pytanie:
Czy układ pamięci flash SPI będzie miał takie same problemy z nieatomowymi operacjami zapisu, jak wewnętrzna pamięć EEPROM dsPIC?
Stephen Collings
2014-03-06 22:32:00 UTC
view on stackexchange narkive permalink

Jakiś czas temu Miałem sporadyczne problemy z wewnętrzną pamięcią EEPROM w dsPIC. Od czasu do czasu pewna wartość w pamięci EEPROM była wyzerowana po włączeniu zasilania. Wyśledziłem problem do momentu, gdy chip stracił moc po kroku kasowania w cyklu zapisu, ale przed zakończeniem zapisu. Chodziło o czas wyłączenia zasilania w stosunku do wykonania oprogramowania układowego, który był (podczas normalnej pracy) losowy. Rozwiązałem to, dodając sekcję bufora do mojej pamięci EEPROM, aby zapewnić, że niekompletny cykl zapisu może zostać zakończony po przywróceniu zasilania. Musiałem zmienić zapisy EEPROM w operację atomową.

Teraz używam innego dsPIC bez wewnętrznej pamięci EEPROM i próbuję użyć zewnętrznego układu pamięci flash, aby przechowywać trwałe dane. Zastanawiam się, czy powinnam mieć podobne obawy. Czy powinienem się martwić, że mój zewnętrzny układ flash wyłączy się w trakcie zapisu i utraci dane, i napiszę poprawkę w moim oprogramowaniu, tak jak zrobiłem dla wewnętrznej pamięci EEPROM? Czy też sam chip gwarantuje atomowe operacje zapisu?

Aby uzyskać więcej szczegółów, moja technika buforowania definiuje obszar trwałej pamięci, który składa się z trzech pól: adresu do zapisu, danych do zapisania i flagi GOTOWE . „Zapis” składa się z czterech kroków: zapis do bufora, ustawienie flagi READY, zapis z bufora, wyczyszczenie flagi READY. Po uruchomieniu sprawdzasz flagę GOTOWY. Jeśli jest ustawiona, wykonaj wszystko, co jest w buforze. To działało dobrze w EEPROM, ale nie jestem pewien, czy będzie dobrze działać we flashu.

Pamięć flash jest właściwie trochę gorsza niż EEPROM, ponieważ w zależności od tego, z czego korzystasz (sam flash lub flash ze sterownikiem - twój ma wewnętrzny kontroler) musisz kasować-zapisywać większe bloki niż na EEPROM (który jest zwykle uporządkowany słowem). Zwykle trwa to znacznie dłużej i daje możliwość zepsutych zapisów. Jak można przeczytać w arkuszu danych, wymazywanie sektora może zająć do sekundy (!), Podczas gdy nowe zapisy trwają 15 ms (na chipie). Więc powiedziałbym, że skorzystaj z tego, co zostało wcześniej doradzone: zrób test brownout, zanim napiszesz do flashowania.
FRAM pozwala uniknąć tego problemu kosztem kosztów / dostępności.
Niestety koszt i dostępność to moje główne zmartwienia!
Cztery odpowiedzi:
Dave Tweed
2014-03-06 23:01:43 UTC
view on stackexchange narkive permalink

Nigdy nie słyszałem o chipie pamięci flash (lub procesorze z wewnętrzną pamięcią flash), który miałby wewnętrznie wystarczającą ilość energii, aby zakończyć cykl zapisu (lub kasowania) w przypadku odłączenia zewnętrznego zasilania. Innymi słowy, jeśli nie masz kontroli nad wyłączeniem systemu, zawsze musisz utworzyć protokół, który będzie w stanie wykryć i obsłużyć każdą indywidualną operację aktualizacji flash, która mogła zostać przerwana.

Jednym ze sposobów obejścia tego jest zapewnienie niezbędnego magazynowania energii (np. kondensatora elektrolitycznego) na twojej płycie, tak abyś mógł wykryć zewnętrzną awarię zasilania, a mimo to wykonać operację zapisu / kasowania, która mogła już być

EDYCJA: Twoja koncepcja bufora zapisu może być używana z zewnętrzną pamięcią flash, ale musi zostać zmodyfikowana, aby uwzględnić większą szczegółowość wymazywania. Zgodnie z arkuszem danych, minimalny rozmiar wymazywania to jeden „sektor” (4 KB).

Będziesz musiał zarezerwować trzy sektory na bufor zapisu. Jeden z nich będzie trzymał twoją flagę READY (nazwij to wektorem WB_R). Drugi będzie zawierał adres sektora aktualizowanego sektora (nazwij go sektorem WB_A). Trzeci będzie zawierał zaktualizowane dane dla tego sektora (nazwij go sektorem WB_D).

Aby zaktualizować dowolny bajt (lub grupę bajtów w jednym sektorze), wykonaj następujące kroki. Zakładamy, że WB_R jest już wymazane.

  1. Usuń WB_A.
  2. Zlokalizuj sektor flash zawierający bajt, który chcesz zmienić (nazwij go sektorem DEST).
  3. Wpisz adres sektora DEST do WB_A.
  4. Usuń WB_D.
  5. Skopiuj zawartość DEST do WB_D, ale kiedy dojdziesz do bajtów, które zmieniasz, zapisz nowe wartości do WB_D zamiast starych.
  6. Ustaw flagę READY w WB_R. Zauważ, że oznacza to, że zmienisz go na stan nieusunięty. Skasowany stan to 0xFF, co oznacza, że ​​piszesz 0x00.
  7. Erase DEST (pobieranie adresu sektora z WB_A).
  8. Skopiuj zawartość WB_D do DEST.
  9. Usuń WB_R.

Po włączeniu sprawdź flagę READY i jeśli jest ustawiona (cokolwiek innego niż 0xFF - mógł zostać tylko częściowo zapisany lub częściowo skasowany), przejdź bezpośrednio do kroku 7.

Zauważ, że w przypadku tego algorytmu każdy z sektorów bufora zapisu jest zapisywany i kasowany przynajmniej raz dla każdej operacji zapisu, zrobić. Może to stać się problemem, jeśli wykonasz dużo (ponad 100 000) zapisów przez cały okres użytkowania produktu. W takim przypadku będziesz potrzebować bardziej wyrafinowanego algorytmu równoważenia zużycia.

Moja technika buforowanego zapisu również powinna działać, prawda?
Nie wiem, musiałbyś to opisać bardziej szczegółowo. Wygląda na to, że możesz polegać na fakcie, że tak się składa, że ​​twoja płyta ma wystarczającą ilość energii, aby ukończyć określone rodzaje cykli zapisu.
Dodałem do pytania opis mojej techniki buforowania. Nie sądzę, żeby magazynowanie energii miało jakikolwiek wpływ na to, co robię, ale mogę się mylić.
OK, ponieważ zapisy jednobajtowe są idempotentne (tj. Nie ma znaczenia, czy są wykonywane więcej niż raz), wygląda na to, że bufor zapisu może działać. Jedynym trybem awarii, jaki widzę, jest awaria zasilania podczas zapisywania adresu lub danych do bufora zapisu lub ustawiania flagi READY, ten konkretny zapis nigdy nie nastąpi - ale przynajmniej adres docelowy nie zostanie usunięty. Ta technika może działać również z zewnętrzną pamięcią flash, ale będziesz musiał wziąć pod uwagę większą szczegółowość wymazywania, ponieważ opiera się ona na buforze zapisu, fladze READY i lokalizacji docelowej, którą można indywidualnie usunąć.
Nie mam 100% pewności co do szczegółowości wymazywania. Czy dobrze rozumiem, że flash z natury usuwa duże bloki i przepisuje wszystkie części, które się nie zmieniły? Jeśli tak, mój pomysł na bufor zdecydowanie nie zadziała ...
Nie, w przypadku większości zewnętrznych urządzeń flash (są wyjątki), wymazywanie i zapisywanie musi odbywać się jawnie jako dwa oddzielne kroki, a Ty jesteś odpowiedzialny za tymczasowe przechowywanie danych, które nie ulegają zmianie w operacji odczytu, modyfikacji i zapisu. Zapis można zwykle wykonać po jednym bajcie, ale kasowanie działa na większej „stronie” lub bloku bajtów naraz. Zobacz moją edycję powyżej.
Najwyraźniej korzystałem z niewłaściwej pamięci. EEPROM jest zdecydowanie lepszym wyborem dla mojej aplikacji. Powinienem był zacząć od tego pytania: http: //electronics.stackexchange.com/questions/65503/why-would-one-still-use-normal-eeprom-instead-of-flash Doceń pomoc!
@StephenCollings: lub nvSRAM, który automatycznie przechowuje ładunek z dedykowanego limitu, gdy poziom zapasów podstawowych spada.
Jon Watte
2014-03-07 00:00:30 UTC
view on stackexchange narkive permalink

Zapis buforowany nie jest wystarczający. Musisz wziąć list z systemu plików i baz danych tutaj: potrzebujesz struktury danych w pamięci flash, która może odzyskać "dobry" stan, gdy jeden blok jest uszkodzony.

Typowy sposób na zrobienie tego to ping-pong między dwoma blokami. Spraw, aby ostatnie dwa lub cztery bajty bloków były „numerem seryjnym” bloku, a pozostałe dane w pozostałej części bloku. Kiedy piszesz nowy blok, zwiększ numer seryjny poprzedniego bloku o jeden, pomijając kasowaną wartość „0” (która może wynosić 0xff, w zależności od typu flasha) i zapisz nowy blok z tym numerem seryjnym.

Po uruchomieniu przeczytaj oba bloki i zobacz, który z nich ma późniejszy numer seryjny (biorąc pod uwagę zawijanie z 0xffff-> 0 i ignorowanie pominiętych wartości kasowania). Użyj tego bloku. Możesz również dodać CRC swoich danych, aby sprawdzić, czy nie są uszkodzone w środku (chociaż jeśli umieścisz numer seryjny na końcu, to „nie powinno” stanowić problemu).

Jeśli masz złożone dane, możesz je rozszerzyć w taki sposób, że baza danych lub system plików zaktualizuje drzewo na dysku, a nawet zaimplementuje rejestrowanie zapisu.

Używałem tego podejścia w przeszłości, ale od tego czasu napotkałem urządzenia flash z blokami, które zachowywały się bardzo złowrogo po (prawdopodobnie) częściowym usunięciu.Od tego czasu zacząłem używać co najmniej trzech bloków, aby w dowolnym momencie zawsze można było zidentyfikować ostatni usunięty blok, wykorzystując tylko informacje z pozostałych dwóch bloków].Dwa bloki nie wystarczą, ponieważ usuwany blok może arbitralnie uzyskać wzór bitowy niezbędny do stwierdzenia, że drugi blok jest usuwany jako ostatni.Użycie trzech bloków pozwala uniknąć tego problemu ...
... bo jeśli dwa bloki "oskarżają" się nawzajem, to jeden z nich musi być ostatnim usunięty, a pozostały będzie wiedział, który to był.
jonk
2014-03-06 23:28:43 UTC
view on stackexchange narkive permalink

Jest to obszar, w którym musisz usiąść i dokładnie opracować strategię. Niektóre szczegóły z arkusza danych to:

  1. Kasowanie sektora 4k: \ $ 400ms \ $ najgorszy przypadek
  2. Kasowanie bloku 32k: \ $ 800ms \ $ najgorszy przypadek
  3. Kasowanie bloku 64k: \ $ 1000ms \ $ najgorszy przypadek
  4. zapis strony: \ $ 3ms \ $ najgorszy przypadek
  5. zapis pierwszego bajtu: \ $ 40 \ mu s \ $ najgorszy przypadek
  6. zapis następnego bajtu: \ $ 12 \ mu s \ $ najgorszy przypadek

Jeśli masz kontrolę nad tym szczegółem, prawdopodobnie dobrym pomysłem jest zorganizowanie moc (poprzez zmagazynowany ładunek kondensatora), która utrzyma się w ramach „marginesu opadania” określonego przez użytkownika podczas krytycznych zapisów. Nie musi to być pełna sekunda, jeśli mądrze użyjesz czasu zapisu pierwszego bajtu (nie zapomnij uwzględnić z tym dodatkowych czasów komunikacji / konfiguracji). Możesz zaktualizować tylko jeden lub dwa bajty na specjalnej stronie, która oznacza na przykład, że rozpoczyna się usuwanie bloku lub sektora. Pozwala to określić, czy wystąpiło zanikanie napięcia lub reset, gdzie byłeś ostatnio, abyś mógł zakończyć proces. Możesz potrzebować więcej niż jednej „strony specjalnej”. Ale w każdym razie musisz dokładnie rozważyć wszystkie przypadki!

supercat
2014-03-07 23:44:02 UTC
view on stackexchange narkive permalink

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.



To pytanie i odpowiedź zostało automatycznie przetłumaczone z języka angielskiego.Oryginalna treść jest dostępna na stackexchange, za co dziękujemy za licencję cc by-sa 3.0, w ramach której jest rozpowszechniana.
Loading...