niedziela, 1 kwietnia 2018

PIC32MM - dostęp ATOMOWY do rejestrów peryferyjnych .


W ramach poszerzenia horyzontów poznawczych mikrokontrolera PIC32MM, przyjrzymy się jak została zaimplementowana w nim obsługa operacji atomowych . Operacje atomowe szczególnie nabierają znaczenia tam gdzie występują przerwania , które mogą nadpisać nam przerwane operacje na np rejestrach peryferyjnych .
Najmniej elegancką metodą pozbycia się tego problemu jest wyłączenie przerwań na czas modyfikacji i zapisu do rejestrów peryferyjnych ale rdzeń PIC32MM został wyposażony w bardziej elegancki mechanizm.

Operacje nie atomowe składają się z trzech operacji składowych określanych  skrótem RMW (Read - Modify - Write) . Taką operacją nie atomową jest np. dowolna operacja na rejestrze peryferyjnym np ustawienie stanu na pinie (PORTAbits.RA3 = 1;)

Części składowe takiej operacji wyglądają mniej więcej tak :
  •  odczytaj zawartość rejestru peryferyjnego  do „zmiennej pomocniczej”
  •  dokonaj operacji arytmetyczno-logicznej na tej "zmiennej pomocniczej"
  •  zapisz wartość "zmiennej pomocniczej" z powrotem do rejestru peryferyjnego 

Wada takich operacji nie atomowych obnaża się w przypadku , kiedy operacja na rejestrze peryferyjnym dokonywana jest asynchronicznie w pętli głównej main i  w przerwaniu. Przerwanie, które wystąpi w czasie jednej z operacji składowych i w którym nastąpi modyfikacja tego samego rejestru np zerowanie , nadpisze nam zmiany w rejestrze dokonywane w pętli main .

Aby się przed tym zjawiskiem uchronić w rdzeniu PIC32MM konstruktorzy zaimplementowali bardzo przyjazny dla użytkownika mechanizm nazwany atomowym  dostępem do rejestrów czyli takim dostępem w którym operacja na rejestrze jest "niepodzielna" i żadna asynchroniczna operacja na tym samym rejestrze w przerwaniu nam nie pomiesza szyków.

Implementacja atomowości w PIC32MM jest od strony użytkownika bardzo wygodna i to jest kolejna zaleta tego sprytnego mikrokontrolera.

Przyjrzyjmy się zatem jak wygląda posługiwanie się "atomowością" w PIC32MM:


Każdy rejestr peryferyjny oprócz swojego bazowego rejestru o adresie Register Address, został doposażony w trzy dodatkowe rejestry do operacji atomowych o adresach przesuniętych w/g klucza  

Register Address + 4 (Clear Bits)
Register Address + 8 (Set Bits)
Register Address + 12 (Invert Bits)
 
 Te trzy dodatkowe rejestry stanowią całą implementację atomowości , bajecznie prostej i przyjaznej dla użytkownika. Każdy z dodatkowych rejestrów wykonuje jedną niepodzielną (atomową) operację  :

Clear Bits - zeruje bity rejestru
Set Bits - ustawia bity rejestru
Invert  Bits - zmienia stan bitów rejestru na przeciwny.

Microchip zadbał o to aby użytkownik nie przemęczał się zbytnio w poszukiwaniu w pamięci fizycznych adresów rejestrów do operacji atomowych.
Wszystko jest podane jak na tacy w postaci opisania każdego rejestru w przyjazny sposób w pliku nagłówkowym mikrokontrolera.

Zobaczmy zatem jak się działa na rejestrach atomowych. Rozważmy przykład ustawienia bitów na PORTA. Adresy rejestrów fizycznie zdefiniowane są tak :

PORTA                /* 0xBF802BD0 */
PORTACLR         /* 0xBF802BD4 */
PORTASET         /* 0xBF802BD8 */
PORTAINV         /* 0xBF802BDC */

Załóżmy że chcemy ustawić najmłodszy bit rejestru PORTA, bit ten będzie reprezentował nam konkretnie pin RA0. Cała operacja spowoduje nam ustawienie stanu wysokiego na pinie RA0

sposób nie atomowy :
PORTAbits.RA0 = 1;
lub
PORTA | = (1<<_PORTA_RA0_POSITION) ; // ustaw najmłodszy bit rejestru na 1 czyli na pinie RA0 mamy stan wysoki

sposób atomowy :
PORTASET = 0x01 (zauważmy, że mniej pisaniny niż nie atomowo, pozycje na których wpiszemy "1" będą ustawione na 1 pozostałe bity są "sprzętowo" maskowane i zmiany na nich nie nastąpią)

Załóżmy że chcemy wyzerować najmłodszy bit rejestru PORTA, bit ten będzie reprezentował nam konkretnie pin RA0. Cała operacja spowoduje nam ustawienie stanu niskiego na pinie RA0

sposób nie atomowy :
PORTAbits.RA0 = 0;
lub
PORTA & = ~ (1<<_PORTA_RA0_POSITION)/* ustaw najmłodszy bit rejestru na 0 czyli na pinie RA0 mamy stan niski.*/

sposób atomowy :
PORTACLR = 0x01 (UWAGA !!! to nie błąd wpisujemy "1" tam gdzie chcemy atomowo zerować bity za pomocą rejestru PORTACLR, pozycje na których wpiszemy "1" będą wyzerowane ,pozostałe bity są "sprzętowo" maskowane i zmiany na nich nie nastąpią)

Załóżmy że chcemy zmienić  najmłodszy bit rejestru PORTA na stan przeciwny, bit ten będzie reprezentował nam konkretnie pin RA0. Cała operacja spowoduje nam ustawienie stanu przeciwnego do dotychczasowego  na pinie RA0

sposób nie atomowy :
PORTA ^=(1<<_POTA_RA0_POSITION);  /*zmienia stan bitu na przeciwny*/

sposób atomowy :
PORTAINV = 0x01 (UWAGA !!! to nie błąd wpisujemy "1" tam gdzie chcemy atomowo zmienić stan bitu/bitów na przeciwny za pomocą rejestru PORTAINV, pozycje na których wpiszemy "1" będą zmienione na przeciwne ,pozostałe bity są "sprzętowo" maskowane i zmiany na nich nie nastąpią) Mamy tu do dyspozycji atomowe tooglowanie.

Nie sprawdzałem tego ale mam podejrzenia , że atomowe operacje na rejestrach mają dodatkowe zalety w postaci np.krótszego kodu wynikowego w asemblerze .
Podsumowując działania atomowe na rejestrach peryferyjnych w PIC32MM są naprawdę proste i miłe w dotyku tego aż się chce używać :)


Pozdrawiam
picmajster.blog@gmail.com

2 komentarze:

  1. To chyba to samo co wirtualne porty w nowych Attiny i ATmega-ch, produkcji Microchip-a.

    OdpowiedzUsuń
  2. Obawiam się, że to nie to samo :) i aby uzyskać tam atomowość trzeba przerwania wyłączać :( Ale nie ma co się smucić :) te nowe ATtiny są i tak zajefajne.

    OdpowiedzUsuń