Problem lekko OT, ale w WinAVR ;-)

W dniu 11.06.2009 15:02, Zbych pisze:

No zagladam dosyc czesto przy okazji debuggowania tego co napisalem. Jakis koszmarnych brakow optymalizacji nie zauwazylem. Mozesz podac konkretny przyklad w C, ktory jest zle optymalizowany? To zawsze mozna zglosic jako bug.

Reply to
T.M.F.
Loading thread data ...

T.M.F. pisze:

To zależy co kto uważa za koszmarne. Poniżej parę przykładów, ale wątpię, żeby komuś chciało się to czytać.

Wszystkie przykłady były optymalizowane na rozmiar.

--------------------------------------------------------- Testowanie warunków logicznych:

#define PIN1_IS_LOW (!(PINB & (1<<PINB7))) #define PIN2_IS_LOW (!(PINB & (1<<PINB6))) volatile uint8_t a; asm volatile(";start"); a = (PIN1_IS_LOW || PIN2_IS_LOW); asm volatile(";stop");

Kod wynikowy:

32 ;start 33 ; 0 "" 2 34 .LSM2: 35 /* #NOAPP */ 36 000a 1F99 sbic 35-32,7 37 000c 00C0 rjmp .L2 38 000e 81E0 ldi r24,lo8(1) 39 0010 90E0 ldi r25,hi8(1) 40 0012 00C0 rjmp .L3 41 .L2: 42 0014 83B1 in r24,35-32 43 0016 90E0 ldi r25,lo8(0) 44 0018 26E0 ldi r18,6 45 001a 9695 1: lsr r25 46 001c 8795 ror r24 47 001e 2A95 dec r18 48 0020 01F4 brne 1b 49 0022 8095 com r24 50 0024 9095 com r25 51 0026 8170 andi r24,lo8(1) 52 0028 9070 andi r25,hi8(1) 53 .L3: 54 002a 8983 std Y+1,r24 55 .LVL0: 56 .LSM3: 57 /* #APP */ 58 ; 96 "main.c" 1 59 ;stop

Pomijając już tą nieszczęsną promocję do inta, to najbardziej w tym kodzie rozwaliło mnie wyłuskiwanie 6 bitu.

Delikatna modyfikacja kodu:

if (PIN1_IS_LOW || PIN2_IS_LOW) a = true; else a = false;

daje już coś takiego:

62 /* #NOAPP */ 63 002c 1F9B sbis 35-32,7 64 002e 00C0 rjmp .L4 65 0030 1E99 sbic 35-32,6 66 0032 00C0 rjmp .L5 67 .L4: 68 0034 81E0 ldi r24,lo8(1) 69 0036 8983 std Y+1,r24 70 .LVL1: 71 0038 00C0 rjmp .L6 72 .L5: 73 .LSM5: 74 003a 1982 std Y+1,__zero_reg__ 75 .LVL2: 76 .L6:

---------------------------------------------------------- Przygotowanie ramki na stosie dla 5 parametrów 16-bitowych:

1637 0068 ADB7 in r26,__SP_L__ 1638 006a BEB7 in r27,__SP_H__ 1639 006c 1A97 sbiw r26,10 1640 006e 0FB6 in __tmp_reg__,__SREG__ 1641 0070 F894 cli 1642 0072 BEBF out __SP_H__,r27 1643 0074 0FBE out __SREG__,__tmp_reg__ 1644 0076 ADBF out __SP_L__,r26

Przy dwóch parametrach:

1751 010e 00D0 rcall . 1752 0110 00D0 rcall .

Łatwo policzyć, że poniżej 8 parametrów bardziej opłaca się wersja z rcall (program optymalizowany na rozmiar).

----------------------------------------------------------- Adresowanie struktur i pobieranie adresów z flasha:

typedef struct{ const prog_char * a; const prog_char * b; const prog_char * c; const prog_char * d; }Aqq;

prog_char s[] = "Aqq";

Aqq PROGMEM tab[]={ {s,s,s,s}, {s,s,s,s}, {s,s,s,s}, {s,s,s,s}, };

asm volatile (";start"); for (uint8_t i = 0; i < tab_size(tab); i++){ blabla( pgm_read_word( &tab[i].a ), pgm_read_word( &tab[i].b ), pgm_read_word( &tab[i].c ), pgm_read_word( &tab[i].d ) ); } asm volatile (";stop");

Pierwszy problem to wyliczanie adresu w flashu:

69 .LBB24: 70 0024 F901 movw r30,r18 71 .LVL4: 72 0026 E050 subi r30,lo8(-(tab+4)) 73 0028 F040 sbci r31,hi8(-(tab+4)) 74 /* #APP */ 75 ; 106 "main.c" 1 76 002a 4591 lpm r20, Z+ 77 002c 5491 lpm r21, Z 78 79 ; 0 "" 2

Pomimo, że jest (czy raczej mogłoby być) użyte adresowanie z postinkrementacją, to adresy kolejnych elementów są i tak przed każdym pobraniem na nowo wyliczane. W przykładzie, który podałem daje to 12 niepotrzebnych instrukcji w każdym obiegu tej krótkiej pętli i dodatkowo zajętą parę rejestrów R19:R18.

Drugi problem, to wyliczanie od początku adresu komórki w tablicy przy każdym obiegu pętli:

27 ;start 28 ; 0 "" 2 29 /* #NOAPP */ 30 0000 C0E0 ldi r28,lo8(0) 31 0002 D0E0 ldi r29,hi8(0) 32 .L2: 33 .LBB21: 34 .LBB22: 35 .LSM2: 36 0004 9E01 movw r18,r28 37 0006 83E0 ldi r24,3 38 0008 220F 1: lsl r18 39 000a 331F rol r19 40 000c 8A95 dec r24 41 000e 01F4 brne 1b 42 0010 F901 movw r30,r18 43 .LVL0: [...] 96 .LBE25: 97 0038 0E94 0000 call blabla 98 .LVL8: 99 003c 2196 adiw r28,1 100 .LSM3: 101 003e C430 cpi r28,4 102 0040 D105 cpc r29,__zero_reg__ 103 0042 01F4 brne .L2 104 .LBE21: 105 .LSM4: 106 /* #APP */ 107 ; 108 "main.c" 1 108 ;stop

Wystarczy prosty myk ze wskaźnikiem i pętla się skraca:

asm volatile (";start"); for (uint8_t i = 0; i < tab_size(tab); i++){ const Aqq * p = &tab[i]; blabla( pgm_read_word( &p->a ), pgm_read_word( &p->b ), pgm_read_word( &p->c ), pgm_read_word( &p->d ) ); } asm volatile (";stop");

27 ;start 28 ; 0 "" 2 29 /* #NOAPP */ 30 0000 C0E0 ldi r28,lo8(tab) 31 0002 D0E0 ldi r29,hi8(tab) [...] 87 0022 0E94 0000 call blabla 88 .LVL8: 89 0026 2896 adiw r28,8 90 .LBE23: 91 .LSM3: 92 0028 80E0 ldi r24,hi8(tab+32) 93 002a C030 cpi r28,lo8(tab+32) 94 002c D807 cpc r29,r24 95 002e 01F4 brne .L2 96 .LBE22: 97 .LSM4: 98 /* #APP */ 99 ; 109 "main.c" 1 100 ;stop 1

A podobno gcc ma super optymalizator do pętli (widocznie moja wersja gdzieś go zgubiła).

Reply to
Zbych

Adam Dybkowski pisze:

To chyba nie jest wygórowane wymaganie dla _wyłączonego_ uC (ale potrafiącego się wybudzić po zmianie stanu portów)? STM32 prawie by się łapał, gdy nie to, że prąd w nim rośnie ze wzrostem temperatury.

Spokojnie, nie tylko TI robi takie wynalazki. Atmel ma uC "pico-power", Microchip "nono-watt". Poza tym za 10zł dostaniesz najmniejszego MSP430 (4kB flash, 256B Ram). Za podobne pieniądze mam ATmega324p, która spełnia moje wymagania co do poboru prądu (i ma potrzebne peryferia). Zresztą jaką mam gwarancję, że gcc będzie generowało lepszy kod na msp430 niż na avr? (pamiętasz? polecałeś ARMa ze względu na lepsze wsparcie w gcc). Ruch na liście mailingowej msp430-gcc jest raczej znikomy. Kolejna rzecz, to zakres napięć zasilających. AVR mogę zasilić prosto z li-ion. Do MSP430 musiałbym dodać stabilizator (który też żre prąd na swoje potrzeby).

Reply to
Zbych

Użytkownik Zbych napisał:

Ooooo to, to :-) W programach unikam operacji logicznych na bitach portów, bo wychodzą koszmarki. Czasem zamiast:

if((PINB & (1<<7)) && (PINC & (1<<3))) {...}

lepiej napisać

if(PINB & (1<<7)) if(PINC & (1<<3)) {...}

Z jakiś troszkę wcześniejszych wersji pamiętam, że samo wstawienie operatora ! na bicie portu automatycznie generowało koszmarek z rozkazem in i wyliczaniem stanu bitu na piechotę.

if(PINB & (1<<7)) {...} wyglądało poprawnie z użyciem sbic/sbis, ale już: if(!(PINB & (1<<7))) {...} generowało wspomniany przez Kolegę koszmarek i w zastępstwie używałem: if(PINB & (1<<7)) else {...} i juz było cacy na sbic/sbis

Co do adresowania tablic w pamięci FLASH w pętli to już od dawna stosuję wyłącznie inkrementację wskaźnika, bo te każdorazowe wyliczanie adresu też mnie biło w oczy :-)

P.S. Używam trochę starawej wersji 20070525, bo sprawia mi najmniej problemów. Późniejsze wersje 2008xxxx sprawiły mi zbyt dużo psikusów i trochę się zraziłem. Choć może warto wypróbować jakąś świeżynkę :-)

Pozdrawiam Grzegorz

Reply to
Grzegorz Kurczyk

T.M.F. pisze:

Wyraźnie napisałem, że nie promocja jest tu największym problemem. Zresztą rzutowanie i tak tego nie poprawia.

Specjalnie napisałem, że optymalizacja była ustawiona na rozmiar, więc rcall powinno mieć pierwszeństwo.

Zapomniałem wstawić. Oto kod:

#define tab_size(__a) (sizeof(__a)/sizeof(__a[0]))

Jeśli według ciebie jest źle zdefiniowana, to czekam na poprawki.

Ja na potrzeby przykładu w ogóle jej nie zdefiniowałem (nie jest to potrzebne, disasemblację zrobiłem po kompilacji, bez linkowania).

Chciałeś przykłady, to ci je pokazałem. Nie jest w tym przypadku istotne czy inne kompilatory zrobiłyby to lepiej (a myślę, że IAR byłby lepszy).

Dzięki, już kiedyś próbowałem (na innym porcie gcc). To nie jest robota na 1 dzień, a ja mam swoją pracę.

Reply to
Zbych

T.M.F. pisze:

Eeee, coś musiałeś skopać. Jak kompilator może zoptymalizować coś czego nie widzi?

cały przykład powinien wyglądać tak:

#define tab_size(__a) (sizeof(__a)/sizeof(__a[0]))

void blabla( uint16_t, uint16_t, uint16_t, uint16_t);

typedef struct{ const prog_char * a; const prog_char * b; const prog_char * c; const prog_char * d; }Aqq;

prog_char s[] = "Aqq";

Aqq PROGMEM tab[]={ {s,s,s,s}, {s,s,s,s}, {s,s,s,s}, {s,s,s,s}, };

asm volatile (";start"); for (uint8_t i = 0; i < tab_size(tab); i++){ blabla( pgm_read_word( &tab[i].a ), pgm_read_word( &tab[i].b ), pgm_read_word( &tab[i].c ), pgm_read_word( &tab[i].d ) ); } asm volatile (";stop");

Reply to
Zbych

W dniu 12.06.2009 03:54, Zbych pisze:

Promocja do int wynika ze standardu C wiec trudno tu robić zarzut kompilatorowi. Żeby to ominąć wystarczy explicite typecastowac.

Tu faktycznie jest jakiś problem z optymalizacja. Warto to zgłosić jako Bug.

Tu sprawa nie jest tak oczywista. Co prawda rcall są krótsze w sensie długości kodu, ale zajmuja 3 takty zegara. To co generuje gcc zajmuje niezależnie od długości ramki 9 taktow, czyli 3 rcall. Sytuacja wyglada jeszcze gorzej kiedy PC jest 22 bitowy – wtedy 4 takty. Wiec jak sadze wybrano pewien kompromis pomiedzy długością kodu a czasem egzekucji.

To zalezy zapewne od zdefiniowanej przez ciebie funkcji tab_size(tab). Kompilator za każdym obiegiem petli musi ja wywołać, żeby wilczyć wynik dla danego argumentu – dlaczego? Bo ja źle zdefiniowałeś.

Sprawdziłem to u siebie i nic takiego się nie dzieje. Musiałem zdefiniować dodatkowo dziwna funkcje Babla bo mi kompilator to optymalizował i ją wywalał.

Tu problem jest bardziej dyskretny. C przy adresowaniu elementów struktury używa adresowania z przemieszczeniem. W pierwszym przykładzie pobierasz adresy konkretnego elementu, stad za każdym razem są pobierane pełne adresy. Za drugim razem używasz adresu struktury i offsetów do jej elementów (operator -> generuje offsety). Zauważ, że gdyby pola tej struktury były duże – powyżej 127 bajtów, to i tak zaszłaby konieczność pobrania pełnego adresu, bo offset byłby większy niż maksymalne przemieszczenie. Oczywiście kompilator może sobie z tym poradzić, niestety tu wychodzi jedna z wad gcc – jest to kompilator pod duże procki, a AVR jest tylko specyficzną wspieraną platformą. Żeby ta optymalizacja dobrze zadziałała trzebaby nieźle zamieszać w kodzie gcc. A w sumie jeśli często się ogląda sesje typu połączony C+assembler to człowiek szybko się uczy jaka składnia języka jest optymalna. Akurat przy pętlach ze względu na specyfikę listy rozkazów AVR można się nieźle wykazać.

Jak ci nie pasuje gcc to zaproponuj cos lepszego... nie mowiac juz o tym, ze masz kod źródłowy wiec możesz się wykazać dla potomności :)

Reply to
T.M.F.

To się mocno zmieniło w nowszych wersjach gcc. W wielu przypadkach to się kompiluje do cbi/sbi. Z kolei ja ostatnio używam wyłącznie C++, więc niewykluczone, że są tu pewne różnice w optymalizacji. Inna sprawa, że jesli pisze program skladający się z kilku tysięcy linii to wielu przypadkach efektywność generowanego kodu mnie nie interesuje. A jesli mam miejsca czasowo krytyczne to zawsze mam inline assembler.

Reply to
T.M.F.

"wrzucenie na stos" to wlasnie utworzenie kopii.

Tylko ze w zwyklym C bez obiektowej nadbudowy jest to operacja dosc prosta i szybka, jesli struktura krotka. A jak jeszcze procek ma 16 czy 32 bitowe operacje push ...

Musialby byc IMO az za bardzo sensowny.

Ja tam patrze troche szerzej. Jak juz sie pisze w C to dobrze by bylo nie pisac pod jeden procesor, ktory w dodatku nie ma alternatywnego dostawcy :-)

J.

Reply to
J.F.

U mnie poprawia - najnowszy WinAVR.

Tak, tylko developerzy sa skupieni na Os - tak to bywa, kiedy zasoby sa ograniczone.

To powinno w efekcie dac stala. Kiedy zastapilem wywolanie tab_size stala problem zniknal. Wiec byc moze uzywasz starej wersji?

To wtedy petla sie totalnie zoptymalizowala i wywalilo mi te wywolania w ogole.

IAR zwykle jest lepszy bo jest dostosowany do tej platformy, niestety uniwersalnosc gcc jest tu jego wada. Z drugiej strony wada ta jest kompensowana problemami na ktore sie natkniesz probujac przeniesc kod z IAR na inna platforme.

Owszem. Ale wystarczy nieco wiedzy jak kompilator generuje kod i wszystkie pokazane problemy znikaja. Sa co prawda inne, ktorych tak prosto wyeliminowac sie nie da, ale skoro ich nie pokazales, to pewnie nie sa dla ciebie tak klopotliwe :)

Reply to
T.M.F.

Chyba kolejny mały kwiatek.

int GetEncoder(void) { cli(); int e = *pEncoderValue; sei(); return e; }

zostało przetłumaczone na:

int GetEncoder(void) { cli(); 1476: f8 94 cli int e = *pEncoderValue; sei(); 1478: 78 94 sei 147a: e0 91 3a 01 lds r30, 0x013A 147e: f0 91 3b 01 lds r31, 0x013B return e; } 1482: 80 81 ld r24, Z 1484: 91 81 ldd r25, Z+1 ; 0x01 1486: 08 95 ret

Nie wiem czemu kompilator wywalił pobieranie zmiennej poza sekcję cli/sei ? To samo jest w najnowszej wersji.

Moim skromnym zdaniem powinno być tak:

int GetEncoder(void) { f8 94 cli e0 91 3a 01 lds r30, 0x013A f0 91 3b 01 lds r31, 0x013B

80 81 ld r24, Z 91 81 ldd r25, Z+1 ; 0x01 78 94 sei 08 95 ret

lub tak:

int GetEncoder(void) { e0 91 3a 01 lds r30, 0x013A f0 91 3b 01 lds r31, 0x013B f8 94 cli

80 81 ld r24, Z 91 81 ldd r25, Z+1 ; 0x01 78 94 sei 08 95 ret

Pozdrawiam Grzegorz

Reply to
Grzegorz Kurczyk

A cos tu jest volatile ? Bo moze mial prawo, jesli uwaza ze tak mu wygodniej ..

Reply to
J.F.

Grzegorz Kurczyk pisze:

Tutaj akurat kompilator ma rację - może przestawiać instrukcje do woli póki nie wpływa to na wynik obliczeń (ani cli, ani sei nie wpływa na obliczenia, atomowość nie jest brana pod uwagę). Żeby kompilator nie wywlekał obliczeń poza sekcję krytyczną trzeba zrobić barierę na pamięci. Twój program powinien wyglądać tak:

int GetEncoder(void) { asm volatile ("cli":::"memory"); int e = *pEncoderValue; asm volatile ("sei":::"memory"); return e; }

Lepiej będzie jednak jak użyjesz makr zdefiniowanych w pliku atomic.h

Reply to
Zbych

Użytkownik Zbych napisał:

Pamiętam, że jakaś starsza wersja (chyba z 2006) kompilowała to bez przestawiania. Funkcja jest z biblioteki, którą napisałem dawno temu i sprawdzałem kod wynikowy. W wersji WinAVR20090313 faktycznie pomogło volatile przy definicji wskaźnika int *pEncoderValue. Zaskoczyło mnie, że volatile może być tu przydatne, bo jednak bardziej służy ono do lokalnego wyłączenia optymalizacji dotyczącej danej zmiennej, a nie do zmiany kolejności operacji nie mających nic wspólnego z tą zmienną. Z punktu widzenia zmiennej *pEncoderValue kod jest tak samo optymalny, a niemalże identyczny:

int *pEncoderValue; daje w wyniku: int GetEncoder(void) { 151c: f8 94 cli 151e: 78 94 sei 1520: e0 91 3a 01 lds r30, 0x013A 1524: f0 91 3b 01 lds r31, 0x013B 1528: 80 81 ld r24, Z 152a: 91 81 ldd r25, Z+1 ; 0x01 152c: 08 95 ret }

volatile int *pEncoderValue; daje int GetEncoder(void) { 1520: f8 94 cli 1522: e0 91 3a 01 lds r30, 0x013A 1526: f0 91 3b 01 lds r31, 0x013B 152a: 20 81 ld r18, Z 152c: 31 81 ldd r19, Z+1 ; 0x01 152e: 78 94 sei 1530: c9 01 movw r24, r18 1532: 08 95 ret

Z drugiej strony taka zmiana sekwencji rozkazów sterujących przez kompilator wydaje mi się trochę dziwna, bo w pewnych sytuacjach ma ona wpływ na wartość obliczeń (właśnie aby tego uniknąć blokowałem przerwania). Idąc tym tropem kompilator mógłby "dojść do wniosku", że sekwencję: sbi(PORTB, 1); sbi(PORTB, 2); sbi(PORTB, 3); można zamienić na: sbi(PORTB, 3); sbi(PORTB, 2); sbi(PORTB, 1); lub jeszcze optymalniej (3 takty zegara zamiast 6) na: in r24, PORTB ori r24, 0x0E out PORTB, r24 bo w sumie efekt końcowy (całkowity wynik operacji) jest ten sam: wyjścia 1,2 i 3 portu B zostały ustawione w stan wysoki. Tyle, że jeśli te linie są sygnałami CS/CLK/DATA dla jakiegoś rejestru szeregowego, to już reszta układu tak samo działać nie będzie. Na szczęście nigdy nie zauważyłem aby robił takie podmianki :-)

Dzięki serdeczne za podpowiedź. Pozdrawiam Grzegorz

Reply to
Grzegorz Kurczyk

A teraz coś czego już zupełnie nie rozumiem:

int *pEncoderValue; // celowo bez volatile

int GetEncoder(void) { cli(); int e = *pEncoderValue; sei(); return e; }

void SetEncoderValue(int Value) { cli(); *pEncoderValue = Value; sei(); }

kompiluje się na:

int GetEncoder(void) { 151c: f8 94 cli 151e: 78 94 sei 1520: e0 91 3a 01 lds r30, 0x013A 1524: f0 91 3b 01 lds r31, 0x013B 1528: 80 81 ld r24, Z 152a: 91 81 ldd r25, Z+1 ; 0x01 152c: 08 95 ret } void SetEncoderValue(int Value) { 152e: f8 94 cli 1530: e0 91 3a 01 lds r30, 0x013A 1534: f0 91 3b 01 lds r31, 0x013B 1538: 91 83 std Z+1, r25 ; 0x01 153a: 80 83 st Z, r24 153c: 78 94 sei 153e: 08 95 ret }

Na jakiej podstawie kompilator (najnowszy WinAVR_20090313) stwierdził, że w pierwszej funkcji kolejność operacji nie ma znaczenia, a w drugiej już ma ? Bug czy tzw. "feature" ;-)

Jednak trzeba do tego pliku .lss regularnie zaglądać, bo można się mocno zdziwić przy uruchamianiu maszynki... ;-) Takie przestawienie rozkazu sei w funkcji może być wyjątkowo upierdliwe, bo niby wszystko jest ok i program może całymi dniami działać dobrze i nagle funkcja jednorazowo oddaje nieprawidłową wartość, aby potem znowu długo wszystko było ok.

Pozdrawiam Grzegorz

Reply to
Grzegorz Kurczyk

Grzegorz Kurczyk pisze:

Dlatego napisałem, że do takich rzeczy jak sekcje atomowe są makra zdefiniowane w pliku atomic.h, a ty uparcie chcesz rzeźbić ręcznie (pomijając przy tym barierę).

Zrób to tak:

#include <util/atomic.h>

ATOMIC_BLOCK(ATOMIC_FORCEON) { int e = *pEncoderValue; }

Albo bezpieczniej:

ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { int e = *pEncoderValue; }

Reply to
Zbych

Volatile nie sluzy do optymalizowania, tylko mowi kompilatorowi ze zmienna moze sie zmienic w dowolnym momencie. W zwiazku z czym nie wolno mu optymalizowac kodu zakladajac ze sie nie zmienia.

Bo jak zalozyc ze nie zmienia sie w przerwaniach/samoczynnie, to kompilator moze przeprowadzic bardzo gleboka optymalizacje - np zalozyc ze zmienna jest zawsze 0 i zlikwidowac caly program.

Programisci sa przed tym wyraznie ostrzegani, ze program moze to zrobic, i maja stosownie pisac program. Albo rybki albo akwarium - albo efektywny program, albo robiacy dokladnie to co programista zapisal :-)

Tak to nie, bo to jednak i operacja specjalna i taka "wyjsciowa", ale juz kolejnosc odczytow z portow moglby przestawic.

J.

Reply to
J.F.

T.M.F. pisze:

Volatile w tym przypadku zabezpiecza tylko przed usunięciem i zamianą miejscami instrukcji "cli" i "sei" a nie tego co jest pomiędzy nimi.

Reply to
Zbych

Użytkownik Zbych napisał:

Nie, no nie chcę nic robić uparcie. Sugestie Kolegi bardzo mi pomogły, za co serdecznie dziękuję. Akurat w moim przypadku wystarczyło volatile, ale gdybym nie spojrzał do .lss to żyłbym w błogiej nieświadomości będąc pewnym, że zablokowałem przerwania w krytycznej sekcji programu i zastanawiając się dlaczego program kiksuje raz na ruski miesiąc. Tym bardziej, że w poprzedniej wersji kompilatora kolejność działań była taka jak w źródłówce. Z biblioteki atomic.h nie miałem jeszcze okazji korzystać, ale widzę, że chyba najwyższy czas :-) Nie narzekam na WinAVR, bo to niezły kompilator, ale główny problem w tym, że program, który w danej wersji WinAVR bez problemu się kompilował i co najważniejsze działał poprawnie, w nowszej wywala błędy kompilacji (co nie jest problemem), ale co gorsza kompilacja przechodzi bezbłędnie, tylko program chodzi nie do końca tak jak powinien. Z tego powodu z dużą rezerwą podchodzę do nowych wersji kompilatora. Problem w tym skąd mam wiedzieć (poza "brutalnym" zajrzeniem do pliku .lss), w którym momencie muszę posiłkować się sztuczką typu ATOMIC_BLOCK lub czymś podobnym, bo kompilator może wygenerować kod, nie do końca zgodny z założeniami autora kodu źródłowego. W podanym wcześniej przykładzie w jednej procedurze było źle, a w następnej już dobrze i nijak nie mogę wydedukować z czego to wynika. W przypadku volatile sytuacja jest jasna, ale z tym sei to kompilator zrobił mi psikusa, bo jest to pierwszy taki przypadek. Przecież sekcji cli() {..} sei() używa się dość często. W większych programach mam ich dużo i to jest jak na razie jedyna funkcja, w której takie zjawisko mi wystąpiło (choć po tym kwiatku nie jestem już tego taki pewien i dla pewności skorzystam z rady Kolegi Zbycha i porobię klamerki ATOMIC_BLOK).

Pozdrawiam i dziękuję za udział w dyskusji. Grzegorz

Reply to
Grzegorz Kurczyk

Grzegorz Kurczyk pisze:

Ja mam na to jeden sposób, porównuję pliki lss z dwóch kolejnych wersji kompilatora. Jeśli zmian jest niewiele, to jest to szybsza metoda niż rozpoczynanie od nowa testów programu.

Powinieneś użyć jej wszędzie tam gdzie do tej pory miałeś

cli(); [...] sei();

Reply to
Zbych

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.