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

[ATtiny13][gcc/asm solved]Sterowniczek RGB - o 20 bajtów za dużo

LordBlick 29 Sie 2011 17:23 3873 17
  • #1 9875209
    LordBlick
    VIP Zasłużony dla elektroda
    W ramach gimnastyki z C, postanowiłem stworzyć odpowiednie zadanie dla tego µC, niestety nie jestem jeszcze dość biegły w optymalizacji kodu, mimo, iż kombinowałem różnie (nawet z unią), na przeszkodzie stanął mi nadmiar generowanego kodu o 20 bajtów, czyli 10 słów rozkazowych. RAM-u za to mam pod dostatkiem...
    Oczywiście przeglądałem plik .lss i jest tam parę spraw do wycięcia, np. zbędne powtarzanie adresowania w pgm_space, inicjalizacja .bss, bad_vector, program_stop itp., ale nie mam pojęcia, jak to zrobić bez wstawek w asm.
    Może na tym forum znajdzie się jakiś ciekawy pomysł ? ;)
    Kod:
    Kod: text
    Zaloguj się, aby zobaczyć kod

    Plik .lss w załączniku.
    Przebieg kompilacji:
    $ make
    avr-gcc -I"."  -mmcu=attiny13 -Wall -gdwarf-2 -std=gnu99 -DF_CPU=9600000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT LED_RGB_t13.o -MF dep/LED_RGB_t13.o.d  -c  LED_RGB_t13.c
    avr-gcc -mmcu=attiny13 -Wl,-Map=LED_RGB_t13.map LED_RGB_t13.o     -o LED_RGB_t13.elf
    avr-objcopy -O ihex -R .eeprom  LED_RGB_t13.elf LED_RGB_t13.hex
    avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex LED_RGB_t13.elf LED_RGB_t13.eep || exit 0
    avr-objdump -h -S LED_RGB_t13.elf > LED_RGB_t13.lss
    
    AVR Memory Usage
    ----------------
    Device: attiny13
    
    Program:    1044 bytes (102.0% Full)
    (.text + .data + .bootloader)
    
    Data:          7 bytes (10.9% Full)
    (.data + .bss + .noinit)
    
    Edit: zmiana nazwy tematu w związku ze znalezionym rozwiązaniem...
  • Pomocny post
    #2 9875559
    Andrzej__S
    Poziom 28  
    LordBlick napisał:

    Oczywiście przeglądałem plik .lss i jest tam parę spraw do wycięcia, np. ... inicjalizacja .bss


    Jeśli zmienne nie muszą być inicjalizowane, to może umieścić je w sekcji .noinit? Po dodaniu atrybutu sekcji .noinit do wszystkich zmiennych globalnych rozmiar kodu wyniósł u mnie po skompilowaniu: 1022 bytes (99.8% Full).


    Cytat:

    4.5 The .noinit Section
    This sections is a part of the .bss section. What makes the .noinit section special is that
    variables which are defined as such:
    int foo __attribute__ ((section (".noinit")));
    will not be initialized to zero during startup as would normal .bss data.


    PS. Widzę sporo powtarzających się wartości w RGB_Table. Jeśli nie przewidujesz zmian w tej tablicy (po których każdy kanał będzie miał inną wartość) to można by ją nieco "skompresować".
  • #3 9875573
    INTOUCH
    Poziom 30  
    Zastanów się czy potrzebnie używasz deklaracji zmiennych w przerwaniach
    skoro następujące zmienne masz zdeklarowane w poniższy sposób.

    volatile uint8_t cLED_RGB_PWM;
    volatile uint8_t pLED_RGB_PWM;
    volatile uint8_t LEDR_PWM;
    volatile uint8_t LEDG_PWM;
    volatile uint8_t LEDB_PWM;
    volatile uint16_t cLED_RGB_Delay;

    Miej zmiennych to mniej zużytej pamięci FLASH w programie.
    Spróbuj skompilować pod AvrStudio5 Przy ustawieniu opcji kompilacji "-Os" plik zajmował mi o 4 bajty mniej niż twój
  • #4 9875633
    LordBlick
    VIP Zasłużony dla elektroda
    INTOUCH napisał:
    Miej zmiennych to mniej zużytej pamięci FLASH w programie
    A niby dlaczego ? Zaglądałeś do .lss ?
    INTOUCH napisał:
    Spróbuj skompilować pod AvrStudio5 Przy ustawieniu opcji kompilacji "-Os" plik zajmował mi o 4 bajty mniej niż twój
    Dziękuję za radę, ale:
    1. Nie używam Windows, zresztą 4 bajty wiosny tu nie czyni, nawet się domyślam w którym miejscu może być to zoptymalizowane (3x LED_OFF w przerwaniu zamienione na LED_PORT=0; daje tyle samo oszczędności).
    2. Nie wiem, czy zauważyłeś, ale opcja "-Os" jest włączona...
    Andrzej__S napisał:
    PS. Widzę sporo powtarzających się wartości w RGB_Table. Jeśli nie przewidujesz zmian w tej tablicy (po których każdy kanał będzie miał inną wartość) to można by ją nieco "skompresować".
    Właśnie ta tablica jest i tak za mała, skok zmiany jasności jest trochę zauważalny.
    Andrzej__S napisał:
    Jeśli zmienne nie muszą być inicjalizowane, to może umieścić je w sekcji .noinit?
    Tutaj rzeczywiście to pomaga.
    Z drugiej strony chętnie bym się jeszcze dowiedział, jak zoptymalizować odczyt 3 bajtów po sobie z pgm_space...
  • #5 9875718
    Andrzej__S
    Poziom 28  
    Chodziło mi o to, że zamiast trzech wartości:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Można byłoby użyć dwóch w stylu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    ponieważ dwie ostatnie i tak się powtarzają. W ten sposób na każde 96 bajtów można oszczędzić 32 bajty. Skomplikowałoby to nieco kod, bo co 64 bajty inny kanał miałby wartość odbiegającą od pozostałych, więc trzeba byłoby użyć np. switch(cLocal/64), aby zmienić wartość odpowiedniego kanału. Zwiększy to na pewno nieco rozmiar kodu, ale za to zmniejszy o 256 bajtów rozmiar danych (tablicy), więc jest o co powalczyć. Przy 9,6MHz szybkość wykonywania nie powinna chyba być problemem.
  • #6 9875737
    LordBlick
    VIP Zasłużony dla elektroda
    Wartości w tej tabeli są wzięte z sufitu - docelowo będą dobierane inaczej, jak się uporam na dobre z kodem i przy okazji się czegoś dowiem... ;)
  • #7 9875747
    Andrzej__S
    Poziom 28  
    LordBlick napisał:

    Z drugiej strony chętnie bym się jeszcze dowiedział, jak zoptymalizować odczyt 3 bajtów po sobie z pgm_space...

    ...być może pgm_read_dword do jakiejś unii?
  • #8 9875805
    LordBlick
    VIP Zasłużony dla elektroda
    Już próbowałem, kodu przybywa:
    Kod: text
    Zaloguj się, aby zobaczyć kod
    Dobre wyzwanie, bo doskonale wiem, jak to mogłoby wyglądać w asm (ATtiny nie ma mnożenia):
    Kod: text
    Zaloguj się, aby zobaczyć kod
    Zamiast:
    Kod: text
    Zaloguj się, aby zobaczyć kod
  • #9 9875948
    Andrzej__S
    Poziom 28  
    LordBlick napisał:

    Już próbowałem, kodu przybywa...


    Być może dlatego, że przedtem kompilator optymalizował fragment:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Pomijając zmienną lokalną cPWM_x, ładował dane z flash do rejestrów i zapamiętywał od razu do zmiennych LEDx_PWM. Teraz musi utworzyć lokalną unię i dopiero później kopiuje dane z unii do zmiennych globalnych LEDx_PWM. Może spróbować pominąć zmienne LEDx_PWM i unię uRGB utworzyć jako globalną (oczywiście z atrybutem sekcji .noinit).
    U mnie daje to kolejne 0,4% oszczędności.

    PS. ...chociaż tak optymalnie jak w asm to pewnie i tak nie będzie :)
  • #10 9877314
    LordBlick
    VIP Zasłużony dla elektroda
    Kwestię unii po przemyśleniu uważam za istotną, ale do późniejszego wdrożenia po czymś, co uważam za jeszcze istotniejsze.
    Otóż po przemyśleniach i przypomnieniu sobie pewnej dyskusji ( https://www.elektroda.pl/rtvforum/topic1929410-90.html#9269693 ) i przeczytaniu odpowiedniej części FAQ avr-libc( http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_regbind ), poszedłem jej tropem.
    Oto najistotniejsze fragmenty najbardziej odchudzonego kodu, jaki teoretycznie powinno mi się dać uzyskać:
    Kod: text
    Zaloguj się, aby zobaczyć kod
    Już uradowany go ładuję do µC, ale niestety, kod schudł również o obsługę programowego PWM... :)
    Czyli pozostały do rozwiązania:
    1. Zmuszenie kompilatora to zaangażowania do pracy tych rejestrów, które mu podałem. Dorzucenie atrybutu volatile nie wiem czemu, ale nic tu nie daje, pewnie czegoś nie doczytałem.
    2. Wdrożenie zminimalizowanego odczytu 24-bit z prog_mem (wcześniej wymieniona unia ?), opartego na tych rejestrach.
    3. Wywalenie obsługi __bad_interrupt/_exit i skoku do niego/__stop_program - takie tematy są raczej zbędne dla kogoś obytego z notą katalogową i te kilka bajtów leży odłogiem.
  • #11 9878694
    Andrzej__S
    Poziom 28  
    O umieszczeniu zmiennych w rejestrze też myślałem, jednak w avr-gcc (tylko ten kompilator dla avr znam) jakoś to nie działa zgodnie z oczekiwaniami. Nie wnikałem nigdy zbyt dociekliwie z czego to wynika, podobno optymalizacja może pominąć zapisy i odczyty z/do zmiennych umieszczonych w rejestrach (takie ostrzeżenie otrzymuję, kiedy zadeklaruję volatile register globalnie).
    Przykładowo ten kod powinien działać (przynajmniej działa u mnie w symulatorze), mimo otrzymanego warning'a:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Po usunięciu atrybutu volatile dla zmiennej cLED_RGB_Delay nie ma ostrzeżeń, ale program przestaje działać, jak również przestaje działać, kiedy zmienne LEDx_PWM zostaną umieszczone w rejestrach, nawet jeśli będą zadeklarowane jako volatile.

    Co do punktu 3, nigdy nie zastanawiałem się nawet, czy można te elementy jakoś pominąć w kodzie wynikowym. Jak brakuje mi flasha to biorę większy procesor lub piszę w asemblerze. Chyba trzeba pogodzić się z tym, że pisząc w C zyskujemy lepszą czytelność kosztem objętości kodu wynikowego.
  • #12 9878800
    gaskoin
    Poziom 38  
    Tak btw - co tak naprawdę będzie w tej tablicy? Bo to ona jest głównym "sprawcą" i to w okół niej powinieneś się skupić - czy da się ją jakoś zoptymalizować pod kątem ilości pamięci, zamiast zbierać po kątach po jednym bajcie :)

    Btw - zrobienie ze zmiennej register i tak nie gwarantuje, że w rzeczywistości tak będzie, standart z resztą o tym wyraźnie mówi.
  • #13 9879273
    maly_elektronik
    Poziom 23  
    A może po prostu załadować tablicę do RAM'u :?:
    Skoro jest go aż nadto to poco go oszczędzać :?:
  • #14 9879354
    McMonster
    Poziom 32  
    Żeby załadować tablicę do RAM to i tak trzeba ją najpierw gdzieś nieulotnie zapisać, czyli w pamięci flash lub EEPROMie, a ten drugi w Tiny13 jest za mały.
  • #15 9879433
    maly_elektronik
    Poziom 23  
    Może zamiast:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    napisać po prostu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #16 9879608
    michalko12
    Specjalista - Mikrokontrolery
    Spróbuj skompilować z opcją -mint8, ale musisz wtedy odpowiednio dobrać typy zmiennych bo ta opcja o połowę skraca wszystkie typy podstawowe czyli tam gdzie masz uint16_t wpisz uint32_t.
  • #17 9879628
    McMonster
    Poziom 32  
    Można spojrzeć do kodu asemblerowego dla pewności, ale to chyba powinno zostać najpierw przetworzone przez preprocesor żeby rozwinąć makro, a potem zoptymalizowane do właśnie takiej jednej instrukcji maszynowej przez kompilator.
  • #18 9880057
    LordBlick
    VIP Zasłużony dla elektroda
    No cóż, chcąc, nie chcąc, postanowiłem się nie upierać przy C i napatrzywszy się na najróżniejsze kombinacje w pliku .lss, wracam w tym przypadku do najprostszego znanego mi rozwiązania - asm od Atmela... ;) Oto najistotniejsze fragmenty kodu:
    Kod: text
    Zaloguj się, aby zobaczyć kod
    Całość zajęła 980 bajtów(można zejść jeszcze niżej zastępując wszystkie zmienne rejestrami), a przy okazji poprawiłem niedoskonałość poprzedniej wersji w C - przy maksymalnym wypełnieniu odpowiednie wyjście jest zawsze w stanie wysokim :
    $ ./Build -b
    compile command: "wine ~/Devel/AVR/AvrAssembler2-2.1.41/avrasm2.exe -D LED_DEBUG -FD%Y.%m.%d   -S H:\Devel\AVRsm\LED_RGB\labels_t13.tmp -fI  -o H:\Devel\AVRsm\LED_RGB\LED_RGB_t13.hex -d H:\Devel\AVRsm\LED_RGB\LED_RGB_t13.obj -e H:\Devel\AVRsm\LED_RGB\LED_RGB_t13.eep -I H:\Devel\AVR\AvrAssembler2-2.1.41\Appnotes -I H:\Devel\AVRsm\_include -m H:\Devel\AVRsm\LED_RGB\LED_RGB_t13.map -l H:\Devel\AVRsm\LED_RGB\LED_RGB_t13.lst -W+ie  -c H:\Devel\AVRsm\LED_RGB\LED_RGB_t13.asm"
    AVRASM: AVR macro assembler 2.1.41 (build 1792 Jul 21 2009 12:30:27)
    Copyright (C) 1995-2009 ATMEL Corporation
    
    H:\Devel\AVR\asm\LED_RGB\LED_RGB_t13.asm(3): Including file 'H:\Devel\AVR\asm\_include\MCU/CodeStartT13.asm'
    H:\Devel\AVR\asm\_include\MCU/CodeStartT13.asm(3): Including file 'H:\Devel\AVR\asm\_include\MCU/ATtiny13.h'
    H:\Devel\AVR\asm\_include\MCU/CodeStartT13.asm(18): Including file 'H:\Devel\AVR\asm\_include\SymRegs.inc'
    H:\Devel\AVR\asm\_include\MCU/CodeStartT13.asm(19): Including file 'H:\Devel\AVR\asm\_include\StdMacros.inc'
    H:\Devel\AVR\asm\_include\StdMacros.inc(176): Device ATtiny13 ( Core = " V2 " ) - using " lpm rd , z + / lpm rd , - z / lpm rd , z " .
    H:\Devel\AVR\asm\_include\MCU/CodeStartT13.asm(20): Including file 'H:\Devel\AVR\asm\LED_RGB\Main.asm'
    H:\Devel\AVR\asm\LED_RGB\Main.asm(7): Including file 'H:\Devel\AVR\asm\_include\TimersCounters/TimerDef.inc'
    H:\Devel\AVR\asm\LED_RGB\Main.asm(11): Including file 'H:\Devel\AVR\asm\_include\TimersCounters/TmrCnt0.inc'
    H:\Devel\AVR\asm\_include\MCU/CodeStartT13.asm(93): * TIMER0_COMPA Vector exist
    H:\Devel\AVR\asm\LED_RGB\LED_RGB_t13.asm(4): No EEPROM data, deleting H:\Devel\AVR\asm\LED_RGB\LED_RGB_t13.eep
    
    ATtiny13 memory use summary [bytes]:
    Segment   Begin    End      Code   Data   Used    Size   Use%
    ---------------------------------------------------------------
    [.cseg] 0x000000 0x0003d4    212    768    980    1024  95.7%
    [.dseg] 0x000060 0x000064      0      4      4      64   6.3%
    [.eseg] 0x000000 0x000000      0      0      0      64   0.0%
    
    Assembly complete, 0 errors. 0 warnings
    wine ERRORLEVEL = 0
    Tym niemniej powiększyłem sobie wiedzę o avr-libc... Dziękuję wszystkim za podpowiedzi i pozdrawiam.
REKLAMA