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

[Atmega16][C] Pomijanie kodu

szafek_ml 06 Kwi 2009 11:38 2556 13
  • #1 6380136
    szafek_ml
    Poziom 11  
    Witam.
    Ostatnio zaczęłem bawić się w programowanie up Atmega16.
    W tym celu zassałem sobie AVRStudio z Atmela i WInAVR z avrferaks.
    Po napisaniu programu i stwierdzeniu niepoprawności jego działania ruszyłem program krok po kroku i zauważyłem , że pomija mi pewne części kodu. Ja otworzyłem kod asemblera to okazało się że linijka kodu C wogóle znikłodzie asemblera.
    Co może być przyczyną takiego zachowania?
    Dokładnie chodzi o zmienną Licz

    Kod C:
    #include <AVR/io.h>
    #include <AVR/interrupt.h>
    #include "global.h"
    //************************  DEFINICJE ***************************************
    //************************ Deklaracje zmiennych *****************************
    
    unsigned char Kol_diod, wiersz, Pk_Wy, Diod;
    unsigned char Licz;
    unsigned char Pk_WyTMP;
    //***************************************************************************
    
    
    
    
    int main(void)	// Pętla główna
    	{
    	DDRA=0xff;			// Port A jako wyjścia 
    	DDRB=0b01111111;			// Port B jako wyjścia
    	DDRD=0b00000000;  	// Port D jako wejścia 
    	PORTA=0xff;
    	//sei();				// Zezwolenie na obsługę przerwań
    	Pk_Wy=0x0;
    	Pk_WyTMP=9;
    	u08 x;
    	//	u08 y;
    		Licz=1;
    		do
    			{
    			x++;
    			Licz++;
    			}
    		while (x==250);
    	}	


    Kod asemblera:
    6:       	{
    +0000003E:   EF9F        SER       R25            Set Register
    +0000003F:   BB9A        OUT       0x1A,R25       Out to I/O location
    18:       	DDRB=0b01111111;			// Port B jako wyjścia
    +00000040:   E78F        LDI       R24,0x7F       Load immediate
    +00000041:   BB87        OUT       0x17,R24       Out to I/O location
    19:       	DDRD=0b00000000;  	// Port D jako wejścia 
    +00000042:   BA11        OUT       0x11,R1        Out to I/O location
    20:       	PORTA=0xff;
    +00000043:   BB9B        OUT       0x1B,R25       Out to I/O location
    22:       	Pk_Wy=0x0;
    +00000044:   92100065    STS       0x0065,R1      Store direct to data space
    23:       	Pk_WyTMP=9;
    +00000046:   E089        LDI       R24,0x09       Load immediate
    +00000047:   93800060    STS       0x0060,R24     Store direct to data space
    30:       			Licz++;
    +00000049:   E082        LDI       R24,0x02       Load immediate
    +0000004A:   93800064    STS       0x0064,R24     Store direct to data space
    33:       	}
    +0000004C:   E080        LDI       R24,0x00       Load immediate
    +0000004D:   E090        LDI       R25,0x00       Load immediate
    +0000004E:   9508        RET                      Subroutine return
    33:       	}
    +0000004F:   94F8        CLI                      Global Interrupt Disable
    +00000050:   CFFF        RJMP      PC-0x0000      Relative jump;
  • #2 6380152
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Kompilator nie jest głupi i może śmiało założyć, że JEDNA zmiana wartości x (x++) spowoduje zakończenie pętli (która trwa tylko gdy x==250). Do tego zmienna x oraz Licz nie mają żadnego wpływu na dalszą część programu (bo jej nie ma), więc również zostają częściowo usunięte.

    4\/3!!
  • #3 6382277
    szafek_ml
    Poziom 11  
    "Kompilator nie jest głupi"- no może i nie ale jak się podchodzi się do programowania bez żadnej wcześniejszej wiedzy to tak jest.
    Odkryłem, że w avr studio jest możliwość ustawienia "optymalizacji kodu" . Domyślnie miałem ustawione -Os ( nie mam pojęcia co to oznacza) Wytestowałem pozostałe i okazało się, że kompilator łyknął to co mu napisałem dopiero przy ustawieniu -O0. Niczego nie pomijał.
  • #4 6382652
    Freddie Chopin
    Specjalista - Mikrokontrolery
    -Os to optymalizacja na rozmiar i sugeruję ci tak zostawić. Zrozum, że kompilator zrobił dobrze, tylko ty napisałeś zły program...

    Skoro twoje zmienne nie są dalej używane, to są zbędne = zostają usunięte. Skoro warunek pętli - jakakolwiek by nie była wartość x - będzie spełniony tylko i wyłącznie RAZ, to nie ma potrzeby robić do tego pętli, tylko instrukcje w niej wykonać owy raz. Jeśli warości które zostały zoptymalizowane byłyby później wykorzystwane - np przekazywane jako parametr funkcji - to gwarantuję ci, że kompilator ich nie usunie. Gdyby warunek pętli miał szerszy zakres (czyli np. x<100), to pętla również nie zostałaby usunięta. Jeśli uważasz inaczej, to faktycznie chyba nie masz żadnej wiedzy o programowaniu. Rolą kompilatora jest wygenerowanie optymalnego kodu, a nie przetłumaczenie tego co napisałeś w C "słowo po słowie" na assemblera, bo to możesz sobie zrobić sam, bez kompilatora.

    4\/3!!
  • #5 6384014
    Aro_
    Poziom 15  
    Jeżeli masz ustawioną opcję optymalizacji na Os, a nie chcesz, aby kompilator usunął jakąś zmienną, wystarczy zadeklarować ją jako volatile.
    Np. w twoim przypadku
    volatile unsigned char Licz;

    Jest to informacja dla kompilatora aby nie optymalizował tej zmiennej. Zresztą zobacz jak będzie wyglądał kod wynikowy w asemblerze.
    Czasem przydaje się to podczas testów, a już konieczne jest przy używaniu zmiennych globalnych w przerwaniu.
  • #6 6384336
    mirekk36
    Poziom 42  
    Aro_ - bardziej przychyliłbym się do wytłumaczenia tego które podał Freddie Chopin - niż to co ty tu wypisujesz:

    Aro_ napisał:
    Jeżeli masz ustawioną opcję optymalizacji na Os, a nie chcesz, aby kompilator usunął jakąś zmienną, wystarczy zadeklarować ją jako volatile.

    Np. w twoim przypadku
    volatile unsigned char Licz;

    Jest to informacja dla kompilatora aby nie optymalizował tej zmiennej.
    Czasem przydaje się to podczas testów, a już konieczne jest przy używaniu zmiennych globalnych w przerwaniu.


    volatile - głównie przydaje się, bywa wręcz konieczne przy używaniu zmiennej zarówno w przerwaniu jak i poza nim - a nie jak piszesz - czasem się przydaje.

    Poza tym, skoro się deklaruje w przepastnym swoim już kodzie czasem zmienną, która później rzeczywiście nie jest wykorzystana, a jej deklaracja została przez pomyłkę - to czyż nie jest korzystniejsze że optymalizacja -Os wywali właśnię tę deklarację a przy okazji zwolni troszkę kodu i pamięci. Podobnie z optymalizacją niepotrzebnych fragmentów kodu, które nigdy nie mają szans być użyte a programista to przeoczył.

    Taka optymalizacja jest szczególnie potrzebna dla początkująych w C a nie odwrotnie
  • #7 6384821
    Aro_
    Poziom 15  
    mirekk36 napisał:
    Aro_ - bardziej przychyliłbym się do wytłumaczenia tego które podał Freddie Chopin - niż to co ty tu wypisujesz:

    Nie bardzo rozumiem, czy gdzieś zaprzeczyłem wypowiedź od Freddie Chopin?
    Gdzie można jeszcze używać zmiennych volatile? Jakoś nie miałem okazji używać tego specyfikatora dla zmiennych nie wykorzystywanych w przerwaniu, jedynie przy symulacjach programu. Nie biorę pod uwagę wstawek asemblerowych, bo to już inna bajka.
  • #8 6385477
    szafek_ml
    Poziom 11  
    Wracając do problemu.
    Ten kod co wkleiłem to jest desperacki kod mający na celu sprawdzić o co chodzi.
    Pierwotnie spotkałem się z tym problemem w tym przypadku:

    
    for (x=0;x<255,++x)
    {     
            if (bit_is_clear(PIND,4)==0) sbi(Pk_Wy,0); else cbi(Pk_Wy,0);	
    	if (bit_is_clear(PINB,7)==0) sbi(Pk_Wy,1); else cbi(Pk_Wy,1);	
    	if (bit_is_clear(PIND,3)==0) sbi(Pk_Wy,2); else cbi(Pk_Wy,2);	
    	if (bit_is_clear(PIND,5)==0) sbi(Pk_Wy,3); else cbi(Pk_Wy,3);
    	if (bit_is_clear(PIND,6)==0) sbi(Pk_Wy,4); else cbi(Pk_Wy,4);	
    	if (bit_is_clear(PIND,7)==0) sbi(Pk_Wy,5); else cbi(Pk_Wy,5);	
    if (Pk_Wy==pkwytmp)Licz++;
    pwytmp = Pk_Wy;
    }
    PORTB=Licz;
    


    Po milionach kombinacji z tym kodem oraz braku efektów napisałem proste coś (to co wkleiłem wcześniej), żeby skumać co robie źle.
    W tym przypadku "krok po kroku" leciał pierwsze 6 if'ów, ostatni pomijał , wpis do pkwytmp tez pomijał i leciał pętlę od początku. BTW zmienna Licz jest później wykorzystywana do zmiany stanów na porcie B.


    Cytat:
    Jeśli uważasz inaczej, to faktycznie chyba nie masz żadnej wiedzy o programowaniu.


    Po raz pierwszy w życiu C zobaczyłem 5 dni temu. Kontakt miałem jedynie z asemblerem i Hitajcem H8 (w dodatku w niewielkim stopniu)
  • #9 6385520
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Pokazujesz skrawki kodu, a ja założę się, że kompilator ma rację, bo komputery się nie mylą, mylą się za to ludzie. Ponieważ programiści gcc mają trochę więcej doświadczenia od wszystkich userów elektrody razem wziętych, stawiam na to, że jednak ty się pomyliłeś...

    Pozatym krokowa praca programu z włączoną optymalizacją ma rzadko kiedy cokolwiek wspólnego z rzeczywistością - instrukcje i zmienne są tak optymalizowane, że rzadko kiedy widać wszystko w symulatorze / debuggerze.

    Zupełnie inną sprawą jest sensowność tego kodu, bo nie wiem jaki miałoby cel odczytanie kilku pinów w ciągu ułamków sekundy aż 255 razy...

    4\/3!!
  • #10 6385523
    _Robak_
    Poziom 33  
    Jak wylaczysz optymalizacje to instrukcje beda sie wykonywaly wszystkie po kolei. Generalnie jak chce sobie przetestowac program w symulatorze to go kompiluje bez optymalizacji i jest wszystko tak jak ma byc ;) Jeszcze nie zdazylo mi sie zeby kompilator pominal jakis kawalek kodu mimo ze z symulacji by tak wynikalo (tylko kilka krokow bo nie chce mi sie sledzic calosci). Podsumowujac, jak chcesz przesledzic program wylacz optymalizacje i juz :)
  • #11 6386092
    szafek_ml
    Poziom 11  
    W takim bądź razie pozostało mi jeszcze sporo nauki C i logiki towiorzenia programów w tym języku.
    Z po co akurat takie odczytywanie? Nieudolny wstęp do programiku eliminującego drgania styków :).Dalsza część programu miała być uzależniona od zmiennej Licz, ale zacząłem kombinować jak pojawiły się problemy.
    Skończy się na tym, że wyeliminuje to sprzętowo nie programowo ;)
  • #12 6386269
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Zamiast tak kombinować, to wystarczy odczytać port, poczekać kilkanaście ms (gotowe funkcje opóźniające w nagłówku util/delay.h) i odczytać raz jeszcze - jeśli stan taki sam, to jest dobrze, jeśli inny - ignor.

    4\/3!!
  • #13 6431087
    jaco9876
    Poziom 1  
    Czy ta optymalizacja kodu -Os na rozmiar polega na jego usuwaniu w przypadku, gdy został nie dokońca dobrze napisany??
  • #14 6431279
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Polega na wielu rzeczach, w tym na usuwaniu kodu, który jest ZBĘDNY. To nie chodzi o to, czy został napisany dobrze czy źle - jeśli nie ma zastosowania (pętle, które nigdy się nie wykonają lub wykonają się raz, warunki które zawsze będą spełnione lub nie będą spełnione nigdy, zmienne, które nie będą już nigdy wykorzystane później, itd.) - jest zbędny - jest usuwany.

    4\/3!!
REKLAMA