Ten artykuł jest kontynuacją tematu testów pamięci RAM rozpoczętego w poprzednim wpisie – link. Poprzednio przybliżyłem trochę ogólnych informacji dotyczących RAMu i jego testowania. Dzisiaj zajmę się omówieniem konkretnych algorytmów. Nie udostępniam swojej implementacji opisanych tutaj testów, podaję za to linki do bibliotek producentów mikrokontrolerów, którzy takie testy zaimplementowali i opisali w notach katalogowych.

Algorytm naiwny

Czasem można się spotkać z bardzo prostym testem RAM polegającym na wpisywaniu konkretnej wartości do komórki, a następnie odczytywaniu jej. Operację tą powtarzamy dla każdej komórki pamięci. Jak można się domyślić – taki test nie jest w stanie wykryć większości błędów. Co więcej, może nawet nie wykryć, czy układ RAM jest wlutowany!  Po wystawieniu danych na wiszące linie, ładunek przez chwilę się na nich może utrzymać i spowodować odczytanie uprzednio wystawionej wartości.

Pattern test

Próbą ulepszenia poprzedniego algorytmu są testy patternów. Aby uchronić się przed wspomnianym wyżej problemem z pojemnością wiszących linii, należy najpierw wpisać wartość do bloku komórek, a następnie je odczytać. Aby test tego typu wykrywał błędy utknięcia na wartości 1/0, przejścia 0->1 lub 1->0, czy wpływu na inną komórkę, test powinien być powtórzony dla różnych wartości wpisywanych do RAMu. Kolejne wartości testowe to właśnie tytułowe patterny, czyli wartości wzorcowe. Powinny one być tak dobierane, żeby przetestować zmiany z 0 na 1 na wielu bitach – czyli, żeby miały duży odstęp Hamminga. Przykładem dobrego patternu testowego są następujące wartości: 0xAA 0x55 0x33 0xCC 0x99 0x66 0x00 0xFF 0x0F 0xF0.

Checkerboard

Test checkerboard polega na wpisaniu do pamięci na zmianę bitów 0 i 1 tak, aby w fizycznej reprezentacji tworzyły szachownicę. Czyli jeśli pierwszy rząd zaczyna się od 0, to kolejny powinien od 1. Po zapisaniu wszystkich wartości testowych sprawdzamy, czy spodziewane wartości są odczytane z odpowiednich komórek, a następnie zapisujemy do pamięci odwrotne wartości i znowu dokonujemy odczytu. Ten test wykrywa błędy utknięcia na wartości i przejścia oraz wpływu wartości bitu na sąsiednie pola.

Algorytmy March – notacja

Jak mogliśmy zauważyć po dotychczas opisanych testach, sprawdzanie RAM polega na zapisywaniu do pamięci pewnych wartości testowych, a następnie odczytywaniu ich. Testy tego typu noszą nazwę testów March. Aby ułatwić opis testów tego typu stworzono do nich specjalną notację:
\Uparrow – przechodzimy po kolejnych adresach od pierwszego do ostatniego.

\Downarrow – przechodzimy po kolejnych adresach od ostatniego do pierwszego.

\Updownarrow – przechodzimy po kolejnych adresach, obojętnie w którą stronę.

w0 – zapisujemy pod danym bitem wartość 0.

w1 – zapisujemy pod danym bitem wartość 1.

r0 – odczytujemy wartość spod danego bitu, spodziewamy się  0.

r0 – odczytujemy wartość spod danego bitu, spodziewamy się 1.
Czyli na przykład zapis:

\Uparrow (w0), \Downarrow (r0, w1)

oznacza, że najpierw zapisujemy na każdy bit wartość 0 zaczynając od początku pamięci. Następnie idąc od ostatniego bitu odczytujemy wartość 0 i wpisujemy tam 1. Jeżeli na którymś bicie nie odczytamy wartosci 0 – test kończymy z błędem.

Rodzaje algorytmów March

Oto kilka popularnych algorytmów typu March:

  1. MATS: \Updownarrow (w0), \Updownarrow (r0, w1), \Updownarrow (r1)
  2. MARCH X: \Updownarrow (w0), \Uparrow (r0, w1), \Downarrow  (r1, w0) \Updownarrow (r0)
  3. MARCH C: \Updownarrow (w0), \Uparrow (r0, w1), \Uparrow (r1, w0), \Downarrow (r0, w1), \Downarrow (r1, w0), \Updownarrow (r0)
  4. MARCH A: \Updownarrow (w0), \Uparrow (r0, w1, w0, w1), \Uparrow (r1, w0, w1), \Downarrow (r1, w0, w1, w0), \Downarrow (r0, w1, w0)
  5. MARCH B: \Updownarrow (w0), \Uparrow (r0, w1, r1, w0, r0, w1),  \Uparrow (r1, w0, w1), \Downarrow (r1, w0, w1, w0), \Downarrow (r0, w1, w0)

Jak widać w algorytmach MARCH A i B podczas jednego przejścia po adresach jest wykonywanych wiele zapisów/odczytów. Algorytmy MARCH A, B i C pozwalają znaleźć błędy utknięcia, przejścia, wpływu na inne komórki oraz błędy linii adresowych, danych i sterujących.

Dla pamięci zorganizowanych słowami (word oriented ram), a nie bitami (bit oriented ram), zarówno notacja, jak i same algorytmy ulegają pewnej zmianie. Wartości zapisywane/odczytywane są wtedy wyrażone w słowach. Na przykład dla słowa o długości 8 bitów notacja może mieć postać:

\Updownarrow$ (w0xAA), \Uparrow (r0xAA, w0x55)

Można przy pomocy takiej notacji zapisać również omawiane wyżej testy Checkerboard i Pattern.

Modyfikacje do testów runtime

Wszystkie przedstawione do tej pory rodzaje testów to testy destruktywne niszczące poprzednią zawartość pamięci. Aby można było je wykonywać w trakcie działania programu wymagają podzielenia na mniejsze części i dodania mechanizmu zapamiętywania poprzedniej zawartości pamięci. Zapamiętywanie danych z pamięci można zrealizować używając bufora znajdującego się poza testowanym obszarem pamięci, albo używając rejestrów procesora. Z kolei podzielenie testów na mniejsze części zmniejsza ich umiejętność znajdowania błędów wpływu na odległe komórki w pamięci.

Test każdych dwóch komórek

Aby test runtime był w stanie wykrywać błędy wpływu na inne komórki można skonstruować test, który sprawdza każdą możliwą kombinację dwóch komórek. Taki test powinien zapisać zadaną wartość do jednej komórki, zapisać negację bitową tej wartości do drugiej komórki i sprawdzić, czy wartość odczytana z pierwszej komórki jest taka sama jak wpisana. Następnie trzeba powtórzyć ten test na odwrotnych wartościach. Dzięki temu możemy przetestować wpływ zera i jedynki na każdym bicie jednej komórki na drugą komórkę. Aby test był jeszcze dokładniejszy można użyć większej ilości patternów takich jak w rozdziale o teście patternowym.

Jednak taki test ma jedną zasadniczą wadę. Aby sprawdzić w ten sposób całą pamięć, potrzeba bardzo dużo iteracji. Ich liczba wzrasta wykładniczo wraz z wzrostem rozmiaru pamięci. Dlatego taki test ma sens jedynie dla procesorów z niewielką ilością RAM, lub jeśli testujemy jedynie pewien sektor pamięci.

Jeżeli znamy fizyczną budowę pamięci możemy zmniejszyć liczbę badanych kombinacji komórek pamięci i ograniczyć się jedynie do badania wpływu danej komórki na jej sąsiadów i komórek o adresach będących potęgą dwójki (w ten sposób wykryjemy błędy adresowania).

Gotowe implementacje testów

Producenci mikrokontrolerów udostępniają biblioteki z implementacjami testów startupowych i runtime’owych na swoje procesory zgodnych z normami bezpieczeństwa. Najczęściej implementowane są testy Class B dla normy 60730. Wraz z bibliotekami można znaleźć noty aplikacyjne tłumaczące zastosowane algorytmy. Poniżej noty aplikacyjne kilku producentów:

Podsumowanie

Algorytmy służące do testowania pamięci RAM nie są zbyt skomplikowane. Polegają zwykle na przechodzeniu po kolejnych adresach i wpisywaniu/odczytywaniu wartości wzorcowych. Dzięki temu możliwa jest ich implementacja w asemblerze. Implementacja testów w C wiąże się z pewnymi problemami, ale przy dodatkowych założeniach jest również wykonalna. Testy runtime mają mniejszą zdolność do wykrywania błędów niż testy startupowe przez fakt, że mamy wtedy do dyspozycji tylko mały fragment pamięci.