logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

AVR-GCC i wolne rejestry, szybkie przerwanie od timera.

Aro_ 26 Gru 2007 10:22 2178 8
REKLAMA
  • #1 4624324
    Aro_
    Poziom 15  
    Posty: 185
    Pomógł: 7
    Ocena: 1
    Witam,
    Musze zrobic szybkie przerwanie od timera w atmega162. W związku z tym nie moge sobie pozwolic na korzystanie z pamięci SRAM i stosu, więc zostają tylko rejestry, wystarczą 3, w tym dwa wskaźnikowe np. X. No więc w właściwym programie te rejesrty nie mogą byc użyte, ani modyfikowane. Czy da się to zrobic w AVG-GCC? Przepisywanie dużego fragmentu kodu na asembler mi nie odpowiada. Może jest jakiś rejestr, którego kompilator wogóle nie używa? R1 niestety odpada ze względu na procedury mnożenia.
  • REKLAMA
  • #2 4624635
    trol.six
    Poziom 31  
    Posty: 1650
    Pomógł: 151
    Ocena: 381
    Pisząc w C to nie masz gwarancji co na końcu zrobi kompilator.

    Możesz przypisać zmienne do rejestru:
    register unsigned char zmienna asm("r2");


    Ale to co sie potem stanie zależy od kompilatora. Mi w wersji 3.4 robił troche badziewia, ze względu na optymalizacje, i nie możność stosowania tej zmiennej jako volatile, Ponoć generowany kod miał sie poprawić w wersji 4.2

    Ale jak braknie kompilatorowi rejestrów, to i tak twoja zmienna wyląduje w SRAM

    A co do ewentualnych stosów, to nie wiem jak zmienić ewentualną procedure odkładania zmiennych na stos przy przerwaniu. Bo czasem rzeczywiście jest to niepotrzebne. A to dodatkowe kilka taktów.
  • REKLAMA
  • #3 4626211
    Aro_
    Poziom 15  
    Posty: 185
    Pomógł: 7
    Ocena: 1
    Jeśli chodzi o samo przerwanie to wywoływane jest oczywiście z atrybutem naked, i całe będzie w asemblerze. Natomiast napisanie kilku KB kodu jako wstawka asemblerowa kompatybilna do reszty programu wyprowadziłoby mnie z równowagi. Wystarczy jak piszę większy algorytm w asemblerze (jako wstawka w C) to i tak wywala sporo błędów, nad którymi mecze się potem całymi dniami.
    Co do przypisywania zmiennym rejestrów to raczej nie przyniesie oczekiwanego efektu, ponieważ kompilator i tak będzie korzystac z pomocniczych rejestrów. Może jednak jest jakis trik pozwalający kompilatorowi ograniczyc ich liczbę. Przecież gdzieś jest zapisane że AVR posiada 32 rejestry. Chyba to nie jest na sztywno w kodzie kompilatora, skoro tym samym kompilatorem można tworzyc dla różnych platform? Nie znam sie na tym zbytnio.
    Jedyną wadą byłoby wtedy ograniczenie ich liczby dla całego programu, ale te 3 rejestry mniej to chyba jakoś ujdzie.
  • #4 4627560
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    Aro_ napisał:

    Co do przypisywania zmiennym rejestrów to raczej nie przyniesie oczekiwanego efektu, ponieważ kompilator i tak będzie korzystac z pomocniczych rejestrów.


    Hmm, nie wiem co masz na myśli? Stosowałem skutecznie technikę wkazaną przez trol.six do zoptymalizowania kodu na tiny2313 (pod gcc 3.4.5), zajmując jeden z rejestrów indeksowych (bodaj Y - r28, r29) jako globalny wskaźnik na strukturę ze zmiennych globalnych. Działało jak najbardziej poprawnie. Kilka uwag:

    1. Taka deklaracja (globalnej zmiennej rejestrowej) musi być widoczna we wszystkich kompilowanych plikach, w tym w bibliotekach. :!: Jeżeli używasz np. avr-libc to musisz ją odpowiednio zmodyfikować i przekompilować.

    2. Może być przydatna opcja -fomit-frame-pointer (podana jawnie).

    3. Zajmowanie rejestru indeksowego na globalny wskaźnik wygląda trochę dziwnie w kodzie, ale działa poprawnie (rezerwuje parę rejestrów):
    
    register struct costam * pCostam asm("r28");
    
  • REKLAMA
  • #5 4627759
    Aro_
    Poziom 15  
    Posty: 185
    Pomógł: 7
    Ocena: 1
    :)Przepraszam, troche inaczej zrozumiałem to przypisywanie. Masz rację, to się sprawdzi, zresztą zobaczę dopiero jak to wygląda w praktyce.
    A to znaczy, że jak użyję jakieś bibliotecznej funkcji to nie weźmie pod uwagę register unsigned char zmienna asm("r2"); ? Czyli jak avr-libc trzeba zmodyfikowac?
    To tak na boku, bo i tak będę używał tylko avr/io i avr/pgmspace w tym fragmencie kodu.
  • #6 4627848
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    Aro_ napisał:

    A to znaczy, że jak użyję jakieś bibliotecznej funkcji to nie weźmie pod uwagę register unsigned char zmienna asm("r2"); ? Czyli jak avr-libc trzeba zmodyfikowac?
    To tak na boku, bo i tak będę używał tylko avr/io i avr/pgmspace w tym fragmencie kodu.


    Nie chodzi o fragment, chodzi o całość! Choć nieprecyzyjnie się wyraziłem.

    Zobacz, jeżeli w czasie kompilacji biblioteki (lub innego fragmentu programu w oddzielnym pliku) kompilator nie zostanie poinformowany o zarezerwowaniu rejestru, to będzie miał pełne prawo użyć go np. jako rejestru pomocniczego przy obliczaniu wyrażeń. Jeżeli ten globalny rejestr jest jednym z rejestrów, które funkcja ma obowiązek przechować, to teoretycznie nie ma problemu, bo kompilator go przechowa (np. na stosie) na czas jego użycia. O ile pamiętam, kompilator zgłasza co najmniej ostrzeżenie w przypadku próby użycia na globalną zmienną rejestrową rejestru z grupy nie przechowywanej przez funkcje (np. r0, r24 - o ile pamiętam, nie udało mi się nigdzie znaleźć dokładnej listy dla AVR).

    Ale w Twoim przypadku jest inaczej. Chcesz użyć tego rejestru w przerwaniu bez przechowywania jego zawartości, czyli chciałbyś zabronić kompilatorowi używania tego rejestru wogóle. Jedynym, znanym mi sposobem uzyskania tego efektu, przynajmniej dla gcc 3.4, jest zadeklarowanie globalnej zmiennej rejestrowej w każdym pliku .c, w tym w bibliotekach. Oczywiście, najlepiej przez umieszczenie deklaracji w headerze.
  • REKLAMA
  • #7 4628990
    Aro_
    Poziom 15  
    Posty: 185
    Pomógł: 7
    Ocena: 1
    ehh, zapomniałem napisac, że przerwanie będzie aktywne tylko podczas wykonywania tego właśnie fragmentu kodu, a więc reszta programu będzie mogła do woli korzystac ze wszystkich rejestrów. Wtedy wystaczy zadeklarowac chyba tylko zmienną jako static dostępną tylko w tej funkcji? (chociaż jeszcze nie wiem czy uda mi sie zrobic z tego funkcję).
  • #8 4631191
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    Aro_ napisał:
    ehh, zapomniałem napisac, że przerwanie będzie aktywne tylko podczas wykonywania tego właśnie fragmentu kodu, a więc reszta programu będzie mogła do woli korzystac ze wszystkich rejestrów.

    W takim wypadku oczywiście wystarczy rezerwacja tylko w tym fragmencie.

    Aro_ napisał:

    Wtedy wystaczy zadeklarowac chyba tylko zmienną jako static dostępną tylko w tej funkcji?


    Chyba nie. Z rozdziału "Specifying Registers for Local Variables":
    Cytat:

    Defining such a register variable does not reserve the register; it remains available for other uses in places where flow control determines the variable's value is not live.

    Sądząc z opisu jest to chyba mało przydatna opcja gcc.

    Ale możesz chyba cały ten fragment umieścić w oddzielnym pliku źródłowym i dodać deklarację tylko tam.
  • #9 4631636
    Aro_
    Poziom 15  
    Posty: 185
    Pomógł: 7
    Ocena: 1
    Przy zadeklarowaniu jako globalna wartośc kompilator posłusznie jej nie stosuje, jednak zadeklarowanie w funkcji nie przynosi żadnego efektu, tak jakby tej zmiennej wogóle nie było. Więc rozwiązanie jest jedno, umiescic ten fragment w osobnym pliku, no ewentualnie zrezygnowac z rejestru w całym programie.
    Dzięki Wam za pomoc!

    Dodano po 1 [godziny] 19 [minuty]:

    Ehh, jednak nie do końca, po skompilowaniu nadal są jeszcze w niektórych miejscach używane te rejestry.
    Czy to wina funkcji bibioltecznych? zadeklarowałem we wszystkich plikach źródłowych zminenna globalna na R23, i kompilator używa go niestety podczas dzielenia. Rejestrów 28 i r29 zadeklarowanych jako wskaźnik globalny używa tylko na starcie, a potem już ich nie rusza.

Podsumowanie tematu

✨ Dyskusja dotyczy implementacji szybkiego przerwania od timera w mikrokontrolerze Atmega162 przy użyciu AVR-GCC, z ograniczeniem do minimalnego użycia rejestrów i bez korzystania z pamięci SRAM oraz stosu. Poruszono problem przypisywania zmiennych do konkretnych rejestrów procesora (np. r2, r23, r28, r29) za pomocą atrybutu asm w GCC, jednak kompilator często ignoruje takie deklaracje lub używa tych rejestrów do własnych celów, zwłaszcza w funkcjach bibliotecznych. Wskazano, że aby rezerwacja rejestru była skuteczna, deklaracje muszą być spójne i widoczne we wszystkich plikach źródłowych, w tym w bibliotekach, co wymaga ich modyfikacji i rekompilacji. Zauważono, że przerwanie z atrybutem naked i kod w asemblerze zapewnia pełną kontrolę nad rejestrami, ale pisanie dużych fragmentów w asemblerze jest uciążliwe. Proponowano umieszczenie kodu przerwania w osobnym pliku źródłowym z dedykowaną deklaracją rejestrów, co może ograniczyć kolizje rejestrów w reszcie programu. Podkreślono, że kompilator AVR-GCC nie oferuje prostego sposobu na całkowite zablokowanie użycia wybranych rejestrów w całym programie, a jedynie lokalne rezerwacje w ograniczonym zakresie. W efekcie, pełna kontrola nad rejestrami w przerwaniu wymaga kompromisów i często ręcznej optymalizacji lub asemblerowych wstawek.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA