Pamięć RAM, jak każdy inny element systemu mikroprocesorowego może się zepsuć. Skutki złego działania RAM mogą być bardzo niebezpieczne. Szczególnie kiedy system wykonuje jakieś odpowiedzialne zadanie. Dlatego testy RAM są wymagane do zapewnienia odpowiedniego SIL (Safety Integrity Level – Poziom Nienaruszalności Bezpieczeństwa) przez różne normy:

  • IEC 60730 – Safety standard for household appliances.
  • IEC 61508 – Functional Safety of Electrical/Electronic/Programmable Electronic Safety-related Systems
  • IEC 61513 – Instrumentation & Control for Systems Important to Safety in Nuclear Power Plants
  • EN 50129 – Railway applications. Communication, signalling and processing systems. Safety related electronic systems for signalling
  • IEC 60601 – Medical electrical equipment – Part 1-2: General requirements for basic safety and essential performance

W ostatnim czasie w pracy spędziłem sporo czasu na pisaniu testów poprawności działania mikrokontrolera, w tym testów pamięci RAM. Postanowiłem więc podzielić się zgromadzoną wiedzą.

Budowa i zasada działania RAM

Przykładowy schemat blokowy budowy pamięci RAM przedstawia poniższy rysunek:

Możemy tutaj wyróżnić:

  • Linie adresowe (A0-An)
  • Linie danych wejściowych (I0-In)
  • Linie danych wyjściowych (O0-On)
  • Linie sterujące (R/W, CS)
  • Matrycę pamięci przechowującą zapisane dane (RAM)

Aby wykonać operacje zapisu na liniach adresowych podajemy adres komórki, pod którą chcemy zapisać. Adres ten jest konwertowany na adres konkretnego rzędu i kolumny w matrycy. Na liniach danych wejściowych podajemy wartość, którą chcemy zapisać. Na liniach sterujących ustawiamy wartości bitowe oznaczające zapis. W tym momencie podana wartość zostaje zapisana pod wybranym adresem.

Operacja odczytu przebiega analogicznie. Na liniach adresowych podajemy adres komórki ,spod której chcemy odczytać wartość. Na liniach sterujących ustawiamy wartości bitowe oznaczające odczyt. W tym momencie wartość z konkretnej komórki w matrycy RAM zostaje zwrócona na liniach danych wyjściowych.

Nowoczesne pamięci RAM na pewno są dużo bardziej skomplikowane, ale ogólna zasada działania pozostaje taka sama.

Rodzaje błędów RAM

Skoro znamy już budowę pamięci RAM, możemy wyszczególnić błędy systematyczne RAM związane z jej wewnętrzną strukturą:

  • Błędy linii adresowych: przerwana ścieżka, zwarcie kilku linii.
  • Błędy linii danych: przerwana ścieżka, zwarcie kilku linii.
  • Błędy linii sterujących: przerwana ścieżka, zwarcie kilku linii.
  • Zapis do jednej komórki w matrycy wpływa na inną.
  • Bit w matrycy zaciął się na jedynce / zerze.
  • Błąd przejścia 0 -> 1 lub 1 -> 0
  • Błąd patternu – komórka działa niepoprawnie, jeżeli sąsiednie komórki tworzą zadany pattern.

Możliwe również są inne błędy takie jak:

  • Złe wlutowanie kości RAM.
  • Uszkodzenie chipa.
  • Problemy z odświeżaniem zawartości w przypadku DRAM.

Wymienione powyżej rodzaje błędów to błędy permanentne. Jeżeli wystąpią, ich efekty powinny być powtarzalne – a przez to możliwe do wykrycia przez odpowiednio skonstruowany test. Oczywiście uszkodzenie chipa może powodować również losowe objawy, a poszczególne linie mogą czasem zwierać, a czasem nie, mogą też występować na nich przesłuchy itp.

Poza błędami systematycznymi mogą zdarzyć się również pojedyncze złe zachowania spowodowane np. promieniowaniem kosmicznym (wbrew pozorom nie jest to tylko tania wymówka dla niepoprawnie działającego systemu – Jak słońce shackowało wybory w Belgii). Wystąpienie takiego błędu może przekłamać dane w RAM, ale nie oznacza, że układ jest zepsuty.

Testy startupowe

Testy startupowe jak sama nazwa wskazuje są wykonywane od razu po uruchomieniu urządzenia. Możemy sobie wtedy pozwolić na wykonanie dłuższych i bardziej wnikliwych testów zanim aplikacja się zainicjalizuje i przejdzie do wykonywania swych zadań. Poza tym możemy wtedy wykonać bezpiecznie test na całej pamięci RAM, ponieważ nie działają jeszcze peryferia, przerwania, czy DMA.

Najbardziej efektywne testy RAM to tak zwane testy destruktywne. Swoją nazwę zawdzięczają temu, że po ich wykonaniu dane przechowywane wcześniej w pamięci są tracone. Z tego powodu nie mogą być wykonywane w trakcie działania aplikacji. Najlepiej je wykonywać jak najszybciej po uruchomieniu procesora. Idealnym miejscem na taki test na mikrokontrolerze jest procedura startupowa przed inicjalizacją sektorów bss i data. Zwykle procedura startupowa zapewnia handler do funkcji wywoływanej przed inicjalizacją pamięci właśnie w tym celu.

Procedurę startupowego testu RAM najlepiej napisać w asemblerze. Próby napisania go w C kończą się ciekawymi błędami. Jeżeli na przykład chcielibyśmy skorzystać z pętli idącej po całej pamięci RAM i zmiennej zapamiętującej aktualny adres, nasz test się wyłoży w momencie testowania komórki pamięci z tą zmienną. Poza tym testowany jest również obszar przeznaczony na stos, więc zniszczeniu ulegają odłożone adresy powrotów z funkcji. Aby uniknąć tych problemów, procedura musi być napisana w asemblerze, a wszystkie zmienne lokalne trzymane w rejestrach procesora. Adres powrotu z funkcji ze stosu również należy przechowywać w rejestrach.

Testy runtime

Testy wykonywane podczas działania aplikacji nie mogą wykonywać się po kilkanaście sekund zatrzymując w tym czasie właściwy program. Zamiast tego musimy jednorazowo testować niewielki fragment pamięci i wykonywać cyklicznie kolejne iteracje testu trwające kilka mikrosekund. Nie możemy sobie również pozwolić na testy destruktywne – każda iteracja musi kończyć się odtworzeniem zawartości pamięci sprzed testu. Te ograniczenia sprawiają, że sprawdzenie wpływu każdej komórki na każdą inną wymaga dużej ilości czasu rosnącej wykładniczo wraz ze wzrostem ilości pamięci.

Kolejnym utrudnieniem jest działanie peryferiów, przerwań i DMA. Pojedynczy test runtime powinien być wystarczająco krótki, aby wyłączenie przerwań na czas jego trwania nie spowodowało błędów w programie. Jeżeli testujemy obszar, na którym działa DMA, również musimy je wyłączyć. W przeciwnym razie testowane komórki mogą być zaktualizowane przez DMA powodując błąd. Wyłączanie kanałów DMA na żądanie jest uciążliwe i może łatwo indukować błędy, za każdym razem trzeba przy okazji wywoływać kod driverów peryferiów korzystających z DMA.

Jeżeli nie chcemy mierzyć się z tymi problemami, możemy wyłączyć z testu niektóre obszary RAM oraz stosować mniej złożone procedury testowe. Jednak wtedy nie będziemy w stanie wyłapać wszystkich możliwych rodzajów błędów. Niestety nie ma tu idealnego rozwiązania i trzeba iść na kompromis.

Możemy sobie ułatwić testowanie, jeżeli znamy fizyczną budowę używanego RAMu. Wtedy w teście runtime zamiast testować wszystkie możliwe kombinacje komórek możemy ograniczyć się do tych, które są najbardziej narażone na błąd – czyli dla każdej komórki badać jej fizyczne sąsiedztwo.

Podsumowanie

W tym tekście omówiłem podstawy teoretyczne dotyczące budowy pamięci RAM i możliwych przyczyn błędów. Omówiłem też dosyć ogólnie testy startupowe i runtime’owe oraz podstawowe problemy, z którymi trzeba się zmierzyć pisząc takie testy. Nie napisałem nic o konkretnych algorytmach, a już wpis wyszedł dosyć długi. Dlatego właśnie algorytmy używane podczas testów RAM omówię w kolejnej części.

Dodatkowe informacje

O testach RAM:

http://www.ganssle.com/testingram.htm

Rodzaje błędów RAM:

http://www.ee.ncu.edu.tw/~jfli/test1/lecture/ch07

http://eecs.ceas.uc.edu/~wjone/Memory.pdf

Budowa wewnętrzna RAM:

https://iis-people.ee.ethz.ch/~kgf/aries/5.html