znaczacy > misc.* > misc.elektronika

Irek.N. (10.02.2019, 23:32)
Pytanie do znawców C. Czy zapis taki:

volatile unsigned int DEL_STEP;

void DELAY(unsigned int Czas)
{
DEL_STEP = Czas;
while(DEL_STEP);
}

może różnić się wykonaniem od zapisu takiego?

void DELAY(unsigned int Czas)
{
unsigned char DEL_MSB,DEL_LSB;
DEL_STEP = Czas;
while(1)
{
DEL_LSB = DEL_STEP/0x100;
DEL_MSB = DEL_STEP^0x100;
if(DEL_MSB == 0 & DEL_LSB == 0)
return;
}
}

Znalazłem błąd w starym kodzie. Ze zdziwieniem odkryłem, że w komendzie
while(DEL_STEP); kompilator sprawdza tylko LSB zmiennej. Oczywiście
generuje to kłopoty, gdy DEL_STEP przekracza wartość 255.
Przepisanie jak niżej rozwiązuje problem, ale nie kumam powodu jego
wystąpienia. Sprawdziłem optymalizacje, to nie to, po prostu kod jest
generowany źle. Ktoś ma pomysł dlaczego kompilator uprościł, a może ja
czegoś nie zauważam?

Miłego.
Irek.N.
ps. DEL_STEP jest modyfikowana w przerwaniu, inaczej procedura nie miała
by sensu.
Zbych (11.02.2019, 00:01)
Irek.N. wrote on 10.02.2019 22:32:
[..]
> }
> Znalazłem błąd w starym kodzie. Ze zdziwieniem odkryłem, że w komendzie
> while(DEL_STEP); kompilator sprawdza tylko LSB zmiennej.


Pokaż cały kod wynikowy funkcji DELAY. Oczywiście kompilator może mieć
błąd, ale dużo bardziej prawdopodobne jest że coś źle interpretujesz.
Mateusz Viste (11.02.2019, 00:41)
On Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. wrote:
[..]
> if (DEL_MSB == 0 & DEL_LSB == 0) return;
> }
> }


O "Keil" nigdy nie slyszalem, ale C obilo mi sie juz o uszy, wrzuce moje
3 grosze, byc moze jakkolwiek pomocne :)

Pierwszy wariant wydaje sie bardzo sensowny. Kompilator zepsuty? A moze
typ int nie jest atomiczny, i kompilator sprawdza najpierw jednego nibbla,
i dopiero jesli ten okaze sie zerowy to zajmie sie drugim?
Próbowales moze zastapic jakims sig_atomic_t?

Ten drugi przyklad to w ogóle lichy jest, bo petla ma teoretyczne prawo
zostac przerwana za kazdym razem kiedy wartosc DEL_STEP zostaje zmieniona
z wartosci o zerowym MSB do wartosci o zerowym LSB (np. 0x0034 -> 0x1200)
- a to dlatego, ze los moze zlosliwie zechciec wykonac twoje przerwanie
akurat pomiedzy liniami:

DEL_LSB = DEL_STEP/0x100;
DEL_MSB = DEL_STEP^0x100;

Nazewnictwo jest tu nieco pomieszane, bo DEL_LSB bedzie faktycznie
zawieral 8 "wazniejszych" bitów, tj. MSB. A DEL_MSB bedzie zawieral 8
"mniej waznych" bitów - z reszta celowosc operacji XOR calkiem mi tu
umyka, tym bardziej ze operuje na bitach których DEL_MSB i tak nie
zobaczy (bo jest charem). Moze zamiast '^' (xor) miales na mysli
'%' (mod)? Tez byloby niepotrzebnie, ale bylbym wtedy w stanie zrozumiec
co autor mial na mysli.

Swoja droga, troche dziwnie patrzec na dzielenie w takiej operacji. Jesli
kompilator madry, to zoptymalizuje twoje "/ 0x100" do ">> 8", ale jesli
na to nie wpadnie... to bedzie malo ekologicznie.

Mateusz
J.F. (11.02.2019, 00:49)
Dnia Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. napisał(a):
> Pytanie do znawców C. Czy zapis taki:
> volatile unsigned int DEL_STEP;
> void DELAY(unsigned int Czas)
> {
> DEL_STEP = Czas;
> while(DEL_STEP);
> }
> może różnić się wykonaniem od zapisu takiego?


> Znalazłem błąd w starym kodzie. Ze zdziwieniem odkryłem, że w komendzie
> while(DEL_STEP); kompilator sprawdza tylko LSB zmiennej. Oczywiście
> generuje to kłopoty, gdy DEL_STEP przekracza wartość 255.
> Przepisanie jak niżej rozwiązuje problem, ale nie kumam powodu jego
> wystąpienia. Sprawdziłem optymalizacje, to nie to, po prostu kod jest
> generowany źle. Ktoś ma pomysł dlaczego kompilator uprościł, a może ja
> czegoś nie zauważam?


A moze na tym procesorze int jest 8-bitowy ?

J.
J.F. (11.02.2019, 00:55)
Dnia Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. napisał(a):
> Pytanie do znawców C. Czy zapis taki:


> volatile unsigned int DEL_STEP;
> void DELAY(unsigned int Czas)
> {
> DEL_STEP = Czas;
> while(DEL_STEP);
> }


> Znalazłem błąd w starym kodzie. Ze zdziwieniem odkryłem, że w komendzie
> while(DEL_STEP); kompilator sprawdza tylko LSB zmiennej.


ewentualnie ... kompilator potraktowal to jako wartosc logiczna, i
uznal ze mu LSB wystarczy, albo wrecz ma niejawny typ logiczny,
8-bit, dokonal konwersji i sprawdzenia ... i mu sie MSB zoptymalzowal.

Sprobuj
while(DEL_STEP != 0);

J.
Grzegorz Niemirowski (11.02.2019, 00:59)
J.F. <jfox_xnospamx> napisał(a):
> A moze na tym procesorze int jest 8-bitowy ?
> J.


int musi mieć conajmniej 16 bitów, nawet na 8-bitowcu. Natomiast nie musi
być obsługiwany atomowo.
Grzegorz Niemirowski (11.02.2019, 01:10)
Irek.N. <tajny> napisał(a):
[..]
> Irek.N.
> ps. DEL_STEP jest modyfikowana w przerwaniu, inaczej procedura nie miała
> by sensu.


LSB to least significant bit. Wątpię aby sprawdzany był najmniej znaczący
bit, może chodziło o bajt. Szkoda, że nie ma kodu asemblerowego. W każdy
razie to też wątpliwe, raczej sprawdzane jest wszystko, tylko pewnie nie
naraz. Jeśli procesor jest 8-bitowy, to int ma 16 bitów i obsługiwany jest
po bajcie a nie atomowo. Czyli de facto są dwa testy, dla młodszego i
starszego bajtu. Jeśli przerwanie nastąpi między testami to mamy problem.
Np. jeśli mamy wartość 256, to test młodszego bajtu da 0. Następnie
przerwanie zmieni wartość na 255. Wówczas wykonany zostanie test starszego
bajtu i też da zero. A więc zostanie uznane, że cała zmienna ma wartość
zero, skoro testy obu bajtów wykazały zera. W takich sytuacjach stosuje się
sekcje krytyczne.
Irek.N. (11.02.2019, 01:11)
> Pierwszy wariant wydaje sie bardzo sensowny. Kompilator zepsuty? Amoze
> typ int nie jest atomiczny, i kompilator sprawdza najpierw jednego nibbla,
> i dopiero jesli ten okaze sie zerowy to zajmie sie drugim?
> Próbowales moze zastapic jakims sig_atomic_t?


Nie, na razie nic nie próbowalem.

> Ten drugi przyklad to w ogóle lichy jest, bo petla ma teoretyczne prawo
> zostac przerwana za kazdym razem kiedy wartosc DEL_STEP zostaje zmieniona
> z wartosci o zerowym MSB do wartosci o zerowym LSB (np. 0x0034 -> 0x1200)
> - a to dlatego, ze los moze zlosliwie zechciecwykonac twoje przerwanie
> akurat pomiedzy liniami:
> DEL_LSB = DEL_STEP/0x100;
> DEL_MSB = DEL_STEP^0x100;


To prawda, ale przynajmniej pozwolilo mi potwierdzic przyczyne problemów.
W ogóle kiepsko to przepisalem, znaczek modulo mi sie omsknal i jak sam
zauwazyles - zamienilem polówki. Sorki. Zamysl byl taki jak podejrzewales.

> Swoja droga, troche dziwnie patrzec na dzielenie w takiej operacji. Jesli
> kompilator madry, to zoptymalizuje twoje "/ 0x100" do ">> 8", ale jesli
> na to nie wpadnie... to bedzie malo ekologicznie.


Radzi sobie z takimi zapisami sensownie.

THX, postaram sie wrzucic jak tylko bede móglkod wynikowy tego fragmentu.

Milego.
Irek.N.
Irek.N. (11.02.2019, 01:14)
J.F. pisze:
> Dnia Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. napisał(a):
> ewentualnie ... kompilator potraktowal to jako wartosc logiczna, i
> uznal ze mu LSB wystarczy, albo wrecz ma niejawny typ logiczny,
> 8-bit, dokonal konwersji i sprawdzenia ... i mu sie MSB zoptymalzowal.
> Sprobuj
> while(DEL_STEP != 0);
> J.


Sorki, źle napisałem na grupie podział na połówki, jak Mateusz zauważył.
Powinno być:

DEL_MSB = DEL_STEP/0x100;
DEL_LSB = DEL_STEP%0x100;

Choć intencja jest oczywista i niczego to raczej nie zmienia, wypada
poprawić.

Sprawdzę Twoją propozycję jutro. Może naprowadzi nas na powód.

Miłego.
Irek.N.
Irek.N. (11.02.2019, 01:28)
> LSB to least significant bit.

Tak nazwane są zmienne i może niefortunnie napisałem LSB.

Wątpię aby sprawdzany był najmniej
> znaczący bit, może chodziło o bajt.


Sprawdzana jest zmienna DEL_STEP ale tylko w zakresie 8 mniej znaczących
bitów.

> Szkoda, że nie ma kodu asemblerowego. Postaram się jutro podać.


> W każdy razie to też wątpliwe, raczej sprawdzane jest
> wszystko, tylko pewnie nie naraz. Jeśli procesor jest 8-bitowy, to int
> ma 16 bitów i obsługiwany jest po bajcie a nie atomowo. Czyli de facto
> są dwa testy, dla młodszego i starszego bajtu. Jeśli przerwanie nastąpi
> między testami to mamy problem. Np. jeśli mamy wartość 256, to test
> młodszego bajtu da 0. Następnie przerwanie zmieni wartość na 255.
> Wówczas wykonany zostanie test starszego bajtu i też da zero. A więc
> zostanie uznane, że cała zmienna ma wartość zero, skoro testy obu bajtów
> wykazały zera. W takich sytuacjach stosuje się sekcje krytyczne.


Zgoda, Mateusz też to zauważył. Ale to nie jest istotne w tym przypadku.
Warunek jest sprawdzany poza przerwaniami, a zmienna jest modyfikowana w
przerwaniu. Zastanawiam się, czy może to mieć jakikolwiek znaczenie.
Może należało by sprawdzić najpierw jedną połówkę, później drugą i
ponownie pierwszą, dla pewności.

Miłego.
Irek.N.
Mateusz Viste (11.02.2019, 10:27)
On Mon, 11 Feb 2019 00:11:08 +0100, Irek.N. wrote:
>> Pierwszy wariant wydaje sie bardzo sensowny. Kompilator zepsuty? A moze
>> typ int nie jest atomiczny, i kompilator sprawdza najpierw jednego
>> nibbla,
>> i dopiero jesli ten okaze sie zerowy to zajmie sie drugim?
>> Próbowales moze zastapic jakims sig_atomic_t?

> Nie, na razie nic nie próbowalem.


Dodam, ze jesli teoria z nie-atomicznym intem mialaby sie potwierdzic, to
mam watpliwosci czy jakikolwiek sig_atomic_t (jesli w ogóle taki istnieje
na tym kompilatorze) bedzie w stanie przechowac wiecej jak 8 bitów. Z
drugiej strony, nie-atomiczny int to bylby troche dziwolag... Musialby
byc albo procesor 8-bitowy, albo szyna pamieci. W obecnych czasach wydaje
sie to (chyba?) malo prawdopodobne.

Mateusz
Mateusz Viste (11.02.2019, 10:32)
On Mon, 11 Feb 2019 00:28:20 +0100, Irek.N. wrote:
> Zgoda, Mateusz tez to zauwazyl. Ale to nie jest istotne w tym przypadku.
> Warunek jest sprawdzany poza przerwaniami, a zmienna jest modyfikowana w
> przerwaniu. Zastanawiam sie, czy moze to miec jakikolwiek znaczenie.
> Moze nalezalo by sprawdzic najpierw jedna polówke, pózniej druga i
> ponownie pierwsza, dla pewnosci.


Brzmi troche jak drutowanie. :)
A czy - strzelam - kod wykonujacy sie w przerwaniu nie móglby, zamiast
pastwic sie nad 16-bitowym DEL_STEP, ustawiac jakiegos globalnego,
jednobajtowego 'breakloop = 1'? Wtedy petla ma latwo, a sprawdzanie kiedy
dokladnie ustawic breakloop lezec bedzie w obowiazkach kodu obslugi
przerwania który to kod, niejako z definicji, przerwany byc nie moze.

Mateusz
Grzegorz Niemirowski (11.02.2019, 10:43)
Mateusz Viste <mateusz> napisal(a):
> Musialby
> byc albo procesor 8-bitowy, albo szyna pamieci. W obecnych czasach wydaje
> sie to (chyba?) malo prawdopodobne.


AVR-y nadal trzymaja sie mocno, ale tu pewnie cos innego skoro Keil.
Grzegorz Niemirowski (11.02.2019, 11:02)
Irek.N. <tajny> napisał(a):
> Warunek jest sprawdzany poza przerwaniami, a zmienna jest modyfikowana w
> przerwaniu.


No właśnie. Zaczyna się sprawdzenie warunku, bach przerwanie, kończy się
sprawdzanie warunku.

> Może należało by sprawdzić najpierw jedną połówkę, później drugą i
> ponownie pierwszą, dla pewności.


Można. Można też zastosować sekcję krytyczną czyli wyłączyć przerwania na
moment testu.
- ostatnia strona
Lub też tak jak pisze Mateusz, sprawdzanie wartości licznika przenieść do
obsługi przerwania a w głównym kodzie sprawdzać tylko flagę.

Co to w ogóle za procesor programujesz? 8-bitowy?
Mateusz Viste (11.02.2019, 12:10)
On Mon, 11 Feb 2019 10:02:24 +0100, Grzegorz Niemirowski wrote:
>> Moze nalezalo by sprawdzic najpierw jedna polówke, pózniej druga i
>> ponownie pierwsza, dla pewnosci.

> Mozna.


Niby mozna, ale to dalej bedzie lichota, bo nic nie gwarantuje, ze przy
trzecim sprawdzaniu BAM! znów interrupt nie strzeli.
Ktos móglby powiedziec "no tak, ale to przerwanie wyzwala sie raz na
jakis czas, kilka cykli CPU to za krótko by dwa razy zdazylo sie
wyzwolic" - ale to nie do konca sluszne zalozenie. Moze byc tak, ze
uruchomi sie nasze przerwanie, zaraz za nim jakies obce przerwanie które
robi cos dluuuugo i zaraz po nim znów wraca to nasze, z nowym (zlosliwie
ustawionym) DEL_STEP.

> Mozna tez zastosowac sekcje krytyczna czyli wylaczyc przerwania
> na moment testu.


To tak. Ale moje skromne i niezobowiazujace zdanie jest takie, ze z
_disable() nalezy obchodzic sie tak jak z goto. Sa przypadki gdzie mozna
sie tym pokusic bo cos uprosci i bedzie wszystkim zylo sie lepiej, ale
jesli tylko mozna - lepiej unikac, bo potem czlowiek sie uzalezni i
zacznie produkowac potworki. No i oczywiscie wylaczenie przerwan
poskutkuje tym, ze ich wykonywanie obarczone bedzie jitterem (bo przy
dluzszych sprawach przerwania nam sie zakolejkuja) - a to moze byc, w
niektórych zastosowaniach/warunkach, jakims problemem. Do tego takie
reczne wylaczanie przerwan wprowadza stan, o którym nalezy pamietac (tj.
nie zapomniec o wlaczeniu przerwan z powrotem) - przy wiekszym codeflow
który moze róznymi sciezkami pobiec latwo robi sie wtedy metlik.

Mateusz

Podobne wątki