Elektroda.pl
Elektroda.pl
X
Elektroda.pl
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

AVRGCC & asembler

10 Mar 2004 22:08 4145 14
  • Poziom 11  
    Hi.

    Piszę właśnie mały (ciągle rośnie :)) programik w C i używam kompilatora AVRGCC.
    Czy Ktoś mi podpowie jak do programu w C dodać procedurę obsługi przerwania w asemblerze?

    Mam bardzo mało czasu na odczytanie/zapisanie bramy i kod nagłówka generowany dla SIGNAL(SIG_INTERRUPT0) się nie wyrabia.
    Dysponuję czasem max 1200 ns na podanie/odczytanie bajtu z magistrali.
    ATmega8535 chodzi na wewnętrznym oscylatorze 8MHz.
  • Poziom 32  
    Przecież w C też możesz obslugiwać przerwania i napewno kompilator źle tgo nie zrobi. A napewno nie zapomni o schowaniu ważnych rejestrów na stos.
  • Poziom 11  
    Jasne. Ale robi to tak (fragment pliku *.lst) :
    SIGNAL(SIG_INTERRUPT0)
    {//-AUX_RD : patrz schemat!!!
    82: 1f 92 push r1
    84: 0f 92 push r0
    86: 0f b6 in r0, 0x3f ; 63
    88: 0f 92 push r0
    8a: 11 24 eor r1, r1
    8c: 8f 93 push r24
    8e: 9f 93 push r25
    outb(ADDirPort,0xff); //brama jako wyjście //---właściwa reakcja na int
    90: 8f ef ldi r24, 0xFF ; 255
    92: 8a bb out 0x1a, r24 ; 26
    outb(ADPort,LastAddr);
    94: 80 91 61 00 lds r24, 0x0061
    98: 8b bb out 0x1b, r24 ; 27
    ...
    }

    Jak widać upłynie conajmniej 14 cykli rozkazowych, do wywołania pierwszej mojej instrukcji, co dla OSC=8MHz daje 1750 ns a mam do dyspozycji tylko 1200.
  • Poziom 11  
    Dzięki za podpowiedź. Muszę to przetrawić, ale wstępnie nie widzę tu możliwości modyfikacji nagłówka procedury obsługi przerwania a jego długość jest krytyczna.

    Gdy na '51 pisałem programy używając kompilatora SDCC (http://sdcc.sourceforge.net) to mogłem do definicji funkcji dopisać modyfikator _naked, który pozwalał na generację kodu funkcji samemu, włącznie z odkładaniem rejestrów na stos. Mogłem wtedy odłożyć na stos tylko to, co jest mi potrzebne, napisać ciało procedury w C i problem z głowy.

    Rozwiązałem chyba ten problem wczoraj (na wyjeździe), pisząc procedurę obsługi przerwania w asemblerze. Sprawdzę czy działa i napiszę jtz.
  • Poziom 11  
    Może komuś się przyda jak rozwiązać mój problem.

    Mnie nie pomogło, bo procek z przyczyn chyba konstrukcyjnych reaguje na przerwanie po ok. 1us, czyli przekracza to dopuszczalny czas podania lub odczytu danej z portu.

    Podparłem się HC574, ale nie po to przecież używam uP :(.
  • Poziom 39  
    Czy ktoś zna rozwiązanie tej kwestii? Bo kolega benek33 miał napisać i nie napisał. Czy można samemu stworzyć całą procedurę przerwania w asm?
  • Poziom 30  
    excray napisał:
    Czy ktoś zna rozwiązanie tej kwestii? Bo kolega benek33 miał napisać i nie napisał. Czy można samemu stworzyć całą procedurę przerwania w asm?

    Kod: c
    Zaloguj się, aby zobaczyć kod

    GCC 6.30 Declaring Attributes of Functions napisał:
    naked
    Use this attribute on the ARM, AVR, MCORE, RX and SPU ports to indicate that the specified function does not need prologue/epilogue sequences generated by the compiler. It is up to the programmer to provide these sequences. The only statements that can be safely included in naked functions are asm statements that do not have operands. All other statements, including declarations of local variables, if statements, and so forth, should be avoided. Naked functions should be used to implement the body of an assembly function, while allowing the compiler to construct the requisite function declaration for the assembler.
  • Poziom 39  
    Dziękuję pięknie! O to chodziło. Dla zainteresowanych wrzucam przykładowy kod - obsługa przerwania od komparatora:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    co w asm daje nam śliczny prosty kod:
    Kod: asm
    Zaloguj się, aby zobaczyć kod
  • Moderator Mikrokontrolery Projektowanie
    No więc wcale nie daje ślicznego kodu. Kompilator zakłada, że R1 jest równy 0 - normalnie tak jest, ale że ISR może zostać wywołane np. tuż po wykonaniu instrukcji MUL, albo czegokolwiek co modyfikuje R1, to tak naprawdę do PORTB nie wpisujesz 0, tylko przypadkową wartość, w 99% będzie to zero, ale w 1% nie.
  • Poziom 39  
    Jak dla mnie daje bo obsługę przerwania będę sam pisać w asm dzięki czemu uniknę badziewnego zwyczaju avrgcc - odkładania na stos wszystkiego co mu się nawinie pod rękę po przyjęciu przerwania.
  • Poziom 28  
    Jak już pisać wstawki w ASM, to zamiast używać kodu w stylu (przykład z GCC-AVR Inline Assembler Cookbook):
    Kod: c
    Zaloguj się, aby zobaczyć kod


    moim skromnym zdaniem lepiej dołączyć do projektu plik z rozszerzeniem *.S
    Kod wygląda wtedy mniej więcej tak (bez konieczności używania atrybutu naked):
    Kod: asm
    Zaloguj się, aby zobaczyć kod


    Wydaje mi się, że czytelniej, mimo konieczności używania makr typu _SFR_IO_ADDR(SREG) zamiast zwykłego SREG. Można zresztą w ten sposób pisać także funkcje wywoływane później w plikach *.c, jeśli są np. krytyczne czasowo.

    No chyba, że tego wstawianego kodu będzie naprawdę niewiele :)
  • Moderator Mikrokontrolery Projektowanie
    excray napisał:
    Jak dla mnie daje bo obsługę przerwania będę sam pisać w asm dzięki czemu uniknę badziewnego zwyczaju avrgcc - odkładania na stos wszystkiego co mu się nawinie pod rękę po przyjęciu przerwania.


    Odnosiłem się do kodu, który podałeś. Jest błędny, bo błędnie zakłada, że R1 jest równy 0, a takiej gwarancji nie ma.

    Dodano po 2 [minuty]:

    Andrzej__S napisał:
    Jak już pisać wstawki w ASM, to zamiast używać kodu w stylu (przykład z GCC-AVR Inline Assembler Cookbook):
    Kod: c
    Zaloguj się, aby zobaczyć kod


    moim skromnym zdaniem lepiej dołączyć do projektu plik z rozszerzeniem *.S
    Kod wygląda wtedy mniej więcej tak (bez konieczności używania atrybutu naked):
    Kod: asm
    Zaloguj się, aby zobaczyć kod


    Wydaje mi się, że czytelniej, mimo konieczności używania makr typu _SFR_IO_ADDR(SREG) zamiast zwykłego SREG. Można zresztą w ten sposób pisać także funkcje wywoływane później w plikach *.c, jeśli są np. krytyczne czasowo.

    No chyba, że tego wstawianego kodu będzie naprawdę niewiele :)


    Problem z twoim przykładem polega na tym, że jest nieoptymalizowalny. Jeśli np. w miejscu wywołania funkcji inne rejestry są akurat wolne, to kompilator nie będzie ich mógł wykorzystać bo twój kod w asm na sztywno zakłada z których rejestrów korzystać. Składnia asm w gcc może początkowo jest pokręcona, ale ma swoje zalety.
  • Poziom 28  
    tmf napisał:

    Jeśli np. w miejscu wywołania funkcji inne rejestry są akurat wolne, to kompilator nie będzie ich mógł wykorzystać bo twój kod w asm na sztywno zakłada z których rejestrów korzystać.

    Jest to pewien mankament tego rozwiązania, bo niestety wymaga odkładania na stos używanych wewnątrz funkcji rejestrów. Chociaż może nie wszystkich, bo dokumentacja avr-libc pisze o rejestrach r18-r27, r30-r31:
    Cytat:

    May be allocated by gcc for local data. You may use them freely in assembler subroutines. Calling C subroutines can clobber any of them - the caller is responsible for saving and restoring.

    Z kolei kompilator podczas wywołania funkcji wie, zawartość jakich rejestrów będzie niepotrzebna w dalszej części programu (kiedy funkcja zakończy działanie) i może zrezygnować z ich zapamiętywania, co zaoszczędzi trochę czasu. Czasami jednak korzyść z napisania funkcji w ASM jest na tyle duża, że strata czasu związana z odłożeniem kilku rejestrów na stos nie jest bardzo znacząca, a czytelność kodu (przynajmniej dla mnie) ją rekompensuje, szczególnie, jak trzeba wrócić do kodu po dłuższym czasie i coś w nim zmienić.

    tmf napisał:

    Problem z twoim przykładem polega na tym, że jest nieoptymalizowalny.

    No akurat z moim przykładem to chyba nie ma takiego problemu, bo dotyczy procedury obsługi przerwania. W tym przypadku chyba nawet kompilator nie wie, w którym miejscu programu nastąpi przerwanie, więc nie jest też w stanie przewidzieć, jakich rejestrów nie trzeba odkładać na stos. Co za tym idzie, to chyba raczej nie ma znaczenia, czy ustalę je "na sztywno", czy wybierze je kompilator, bo i tak ich zawartość trzeba zapamiętać na początku i przywrócić na końcu procedury. A taki "sztywny" kod też ma swoje zalety, bo mam pełną kontrolę nad programem i mogę pisać naprawdę wydajne procedury obsługi przerwań.

    tmf napisał:

    Składnia asm w gcc może początkowo jest pokręcona, ale ma swoje zalety.

    Ja tego nie neguję. Po prostu dla mnie taki kod jest mało przejrzysty i staram się go unikać. Napisałem zresztą, że to tylko "moje skromne zdanie". Nie ma przymusu go podzielać :)
  • Moderator Mikrokontrolery Projektowanie
    Owszem, jeśli chodzi o ISR to sprawa jest prosta i napisanie wstawki całkowicie w asm jak najbardziej celowe. To co pisałem odnosiło się szczególnie do krótkich funkcji, które kompilator inlinuje - wtedy straty związane nie tyle z zapamiętywaniem rejestrów co koniecznością wykonania instrukcji mov, tak aby dostosować argumenty do rejestrów oczekiwanych przez twoją funkcję mogą być znaczne.
    To może być bardziej bolesne w nowym gcc (>=4.7), gdzie jest nowy alokator rejestrów. Brak informacji o użytych rejestrach (clobber list) uniemożliwi pewne optymalizacje, w efekcie cały kod może mocno ucierpieć, aczkolwiek nie ten fragment pisany w assemblerze. Także IMHO warte umieszczenia w pliku .S (jeśli ktoś się upiera) są dwa rodzaje funkcji:
    - ISR,
    - dłuższe wstawki w assemblerze, wykorzystujące masę rejestrów co i tak by wymagało ich masowego odkładania na stos.
    Należy tylko dosyć dobrze zapoznać się z ABI kompilatora, żeby mu dodatkowo nie utrudniać zadania.