RTOS blokowanie przerwań

Odpowiedz Nowy wątek
2016-06-15 22:00

Rejestracja: 5 lat temu

Ostatnio: 8 miesięcy temu

0

Witam serdecznie forumowiczów,

Mam taki kod źródłowy dla pewnego urządzenia diagnostycznego.
Jest sobie tam zdefiniowany bufor i flaga buffer_full, bufor jest tablicą, flaga zmienną uint8_t
Jeśli flaga zostanie ustawiona mechanizm przerwaniowy wyśle dane z bufora na uart'a po czym skasuje flagę buffer_full.
Oczywiście musi pójść kilka przerwań aby wszystkie dane zostały wysłane.
Zatem dostęp do zmiennej buffer_full jest wymagany z poziomu przerwania.

Mam również RTOS'a i scheduler sterujący wywołaniem zadania o nazwie : rs232_handler.
Zadanie to rozpoczyna się tak:

DISABLE_INTERRUPTS;
buffer_full_temp = buffer_full;
ENABLE_INTERRUPTS;

if ((RESET != buffer_full_temp) || (NO_JOB == current_job_id))
    return;

/* some other code... */

Mam pytanie : jakiż jest sens umieszczania instrukcji przepisywania flagi w sekcji krytycznej w tym wypadku ?
Rozumiem iż jeśli zmienna buffer_full byłaby 16-bitowa mogłoby być tak że na 8 bitowej architekturze załadował by pierwszy bajt i mogłoby pójść przerwanie co zakłóciłoby całkowicie proces odczytu. Jednak tutaj zmienna jest 8 bitowa.

I jeszcze jedno pytanie:
Co oznacza słówko "far" w deklaracji parametru przyjmowanego przez funkcję jak poniżej :
inline uint8_t foo(x_s * far p1, uint8_t p2);
Oczywiście p1 jest wskaźnikiem na strukturę o nazwie x_s jednakże nie kapuję co oznacza te słówko "far".
Jeden z kolegów mówił mi coś na temat tego że to sposób aby procek odnosił się do pamięci zewnętrznej jednakże ciągle nie mogę pojąć jak jest to rozwijane przez kompilator. Czy ktoś z łaski swojej mógłby przybliżyć ten temat ze słówkiem "far" ?

edytowany 1x, ostatnio: Adamos19, 2016-06-15 22:49

Pozostało 580 znaków

2016-06-16 08:53

Rejestracja: 6 lat temu

Ostatnio: 3 godziny temu

0

Z tymi przerwaniami to nie chodzi o możliwość zapisu do buffer_full? To mocno zależy od architektury, ale jedno to zrobić np.
buffer_full_temp = 0, które rozwinie się np. do st &buffer_full, r2 (zakładamy, że r2 jest zawsze zerem) a drugie przepisać jedną zmienną do drugiej. Dołóż jeszcze volatile do flagi (jednej lub obu) i wyjdzie np. coś takiego dla prostej operacji typu flag1=flag2:

ld r3, &flag2
; a teraz niech przyjdzie przerwanie i zmodyfikuje flag2, w rejestrze r3 jest stara wartość
st &flag1, r3

Asembler wziąłem z czaszki, ld to odczyt z pamięci (load), st to zapis (store).

EDIT: bez kontekstu trudno cokolwiek powiedzieć więcej. Jak masz wątpliwości: disasembler i jazda...

A far to rozszerzenie, co się z tym dzieje zależy od architektury. Co to dokładnie za mikrokontroler?

edytowany 1x, ostatnio: alagner, 2016-06-16 08:55

Pozostało 580 znaków

Złoty Lew
2016-06-16 16:10
Złoty Lew
0

No właśnie tutaj nie chodzi o możliwość zapisu do buffer_full ale właśnie o odczyt z niego. Zapis do niego może wystąpić w procedurze przerwania.
W odniesieniu jednak do Twojej wypowiedzi to mam wątpliwości dlatego że niech i przerwanie zmodyfikuje flag2 czyli nasz buffer_full - no i co z tego ?
Przecież po wyjściu z przerwania zwracany jest kontekst więc rejestr z którego następuje odczyt (r3) nie zostanie zmodyfikowany...

Procek to M16C Renesasa. Urządzenie to taki tester diagnostyczny do samochodu...
Co to jest "rozszerzenie" o którym mówisz w kontekście słowa "far" ? To chodzi o rozszerzenie standardu języka ? Jeśli tak, to co to rozszerzenie może oznaczać ?

Pozostało 580 znaków

2016-06-17 08:22

Rejestracja: 6 lat temu

Ostatnio: 3 godziny temu

0

Nie zalogowałeś się ;) W każdym razie: R3 nie zostanie zmodyfikowany, no właśnie. Wobec tego zapiszesz starą (nieaktualną) wartość buffer_full. Moim zdaniem to o to chodzi.

Pozostało 580 znaków

Złoty Lew
2016-06-17 13:53
Złoty Lew
0

Dobra już kapuję. Nie wiem o co chodzi bo ta sekcja krytyczna nic tutaj moim zdaniem nie daje nawet rozumując w sposób w który przedstawiłeś ponieważ gdyby nawet ta instrukcja nie była w sekcji krytycznej czyli zmiany występujące w przerwaniu nie zostałyby uwzględnione to nic takiego by się tutaj nie stało. Ale ok, i tak rozjaśniłeś mi pogląd na to wszystko.

Mam jeszcze jedno pytanie dotyczące rzutowania , promocji typów i ogólnie zmiennych które brzmi :

Czy w przypadku gdy robimy tak:

#define MAKRO 8
uint16_t x;
uint8_t y=200;

a następnie :

x = y * MAKRO;

To czy w takim wypadku istnieje ryzyko że utracimy dokładność obliczeń czy też zawsze będzie tak że wynik zapisany w "x" będzie równy 1600 bez względu na architekturę mikrokontrolera ?
Nie mam czasu sprawdzać jak to zostanie rozwinięte z poziomu assemblera dlatego na szybko proszę forumowiczów o wyjaśnienie.

Pozostało 580 znaków

2016-06-17 14:36

Rejestracja: 6 lat temu

Ostatnio: 3 godziny temu

0

Jeżeli nie masz jakiegoś dziwnego inta o rozmiarze 8 bitów to raczej wszystko powinno być ok.

Pozostało 580 znaków

Złoty Lew
2016-06-17 15:57
Złoty Lew
0

Mógłbyś jaśniej ? Przecież zmienne którymi operujemy wyraźnie mają określone rozmiary przy deklaracji...
uint8_t jest to zmienna 8-bitowa
uint16_t jest to zmienna 16-bitowa

Co chcesz powiedzieć mówiąc "jeśli nie masz jakiegoś dziwnego inta..." bo nie bardzo Cię rozumiem...

Pozostało 580 znaków

2016-06-17 17:09

Rejestracja: 6 lat temu

Ostatnio: 3 godziny temu

0

MAKRO jest typu int (lub dłuższego do jakiego się zmieści) więc powinna nastąpić promocja uint8_t do inta. Jakikolwiek by on nie był. Standard wymaga, żeby mieścił l-by w zakresie przynajmniej -+32767, czyli de facto wychodzi z tego min. 16 bitów (może być więcej). Ale dla takich AVRów np. można zbudować gcc gdzie 1==sizeof(int), generalnie w embedded trafiają się takie cuda czasem, stąd moja uwaga o int'cie.

Pozostało 580 znaków

2016-06-17 18:29

Rejestracja: 5 lat temu

Ostatnio: 8 miesięcy temu

0

Dzięki za wyjaśnienie. Właśnie zrobiłem test dla AVRa 8 bit (atmega16)
Okazało się że dla kodu:

#define MAKRO 8
uint16_t x;
uint8_t y=100;
x=y*MAKRO;
if (x==800) LED = ON;
else LED = OFF;

Dioda mi się zapaliła.

#define MAKRO 8
uint16_t x;
uint8_t y=100;
x=y*((uint8_t)MAKRO);
if (x==800) LED = ON;
else LED = OFF;

Dioda się również zapaliła, dlatego wybacz ale ciągle nie rozumiem.
Wniosek z tego wyciągam taki że w przypadku gdy na 8 bitowej architekturze robimy iloczyn zmiennych typu uint8_t i jego wynik przypisujemy do zmiennej typu uint16_t to kompilator automatycznie dokonuje rzutowania jednej z tych zmiennych na 16 bit.

W przypadku kodu:

#define MAKRO 8
uint16_t x;
uint8_t y=100;
x=(uint8_t)y*((uint8_t)MAKRO);
if (x==800) LED = ON;
else LED = OFF;

To daje ten sam efekt, a zatem robi tak nawet gdy staramy się go zmusić do wykonania operacji na 8 bitach.

Dopiero takie coś:

#define MAKRO 8
uint16_t x;
uint8_t p;
uint8_t y=100;
p=y*MAKRO;
x=p;
if (x==800) LED = ON;
else LED = OFF;

Powoduje że dioda nie zapala się. Zatem aby zmusić kompilator do tego aby wykonywał tą operację na 8 bitach należy wynik przypisywać do zmiennej 8 bit.

A zatem kto mi to może w prostych słowach podsumować. Jak się ma do tego akumulator ? Jak się ma do tego promocja typów o której tyle czytamy ?

Racja, mój błąd. Poszukałem co mówią na ten temat K&R, generalnie można założyć, że wynik to będzie przynajmniej int (jak się nie zmieści to long albo więcej), przypisanie go co najwyżej skróci. I rozwiń o co Ci chodzi z akumulatorem? - alagner 2016-06-17 19:50
Co to znaczy "jak się nie zmieści" ? Zagadałem z "akumulatorem" ponieważ miałem cichutką nadzieję na to że ktoś mi to rozwinie jak takie mnożenie wykonuje Mega16. Miałem nadzieję że ktoś się zlituje i przywoła tutaj instrukcje asemblerowe z użyciem rejestrów, akumulatora jeśli takowy jest w obliczenia zaangażowany aby raz na zawsze rozwiać wątpliwości co do mnożenia dwóch liczb z użyciem mikrokontrolerów 8 bitowych i innych. - Adamos19 2016-06-17 20:58

Pozostało 580 znaków

2016-06-19 18:31

Rejestracja: 5 lat temu

Ostatnio: 8 miesięcy temu

0

Zrobiłem kolejny test, tym razem bardzo prostego, następującego kodu:


#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t i=0;
volatile uint8_t j=0;
volatile uint16_t k=0;
volatile uint8_t p=0;

ISR(TIMER0_OVF_vect)
{
    i++;
}

ISR(INT0_vect)
{
    j++;
}

int main(void)

{
    while (1)
    {
        k = i * j;
    }
}

Wygenerowałem kod assemblera z którego wyciągam następujące wnioski (kompilator avr-gcc dla atmega16, architektura 8 bit):

  1. W przypadku gdy k jest zmienną 16 bitową to pomimo że operandy są 8 bit to wynik jest 16 bit. A zatem to że obydwa operandy są 8 bitowe nie powoduje że wynik będzie skrócony do 8 bitów ponieważ kompilator korzysta z rozkazu MUL który daje właśnie 16 bitowy wynik.

  2. Po zmianie typu zmiennej k na uint8_t kompilator również korzysta z MUL i później ignoruje jeden z rejestrów do którego zapisał wynik mnożenia, tym samym wynik 16 bit jest obcinany do postaci 8 bit i zapisywany do zmiennej k.

  3. Po zmianie typu zmiennej k na uint32_t kompilator znowu korzysta tylko z MUL i w przypadku gdy oba operandy są 8 bit lub gdy przynajmniej jeden z nich jest 16 bit to i tak wynik będzie określony na 16 bit, pozostałe 16 bit zmiennej wyjściowej (dwa najstarsze bajty) będą zerami.

Tutaj mam pytanie bo w tym ostatnim przypadku dostałem:

  a2:   90 91 66 00     lds r25, 0x0066
  a6:   80 91 65 00     lds r24, 0x0065
  aa:   98 9f           mul r25, r24
  ac:   c0 01           movw    r24, r0
  ae:   11 24           eor r1, r1
  b0:   aa 27           eor r26, r26

  b2:   97 fd           sbrc    r25, 7
  b4:   a0 95           com r26
  b6:   ba 2f           mov r27, r26

  b8:   80 93 61 00     sts 0x0061, r24
  bc:   90 93 62 00     sts 0x0062, r25
  c0:   a0 93 63 00     sts 0x0063, r26
  c4:   b0 93 64 00     sts 0x0064, r27

Po co kompilator zrobił te trzy linijki środkowe ?
Rozumiem że jeśli 7 bit rejestru R25 jest zerem to wówczas nie wykona się instrukcja COM więc najstarsze dwa bajty liczby wyjściowej będą tak jak mówię zerami. Ale co jeśli będzie tam bit równy 1. Czy wówczas mam rozumieć najstarsze dwa bajty liczby wyjściowej będą miały same jedynki ? Przecież to trochę bezsensu. Kiedy taka sytuacja się zdarzy ?

Pozostało 580 znaków

2016-06-19 20:19

Rejestracja: 6 lat temu

Ostatnio: 3 godziny temu

0

Coś kręcisz:
https://gcc.godbolt.org/#compilers:!((compiler:avrg453,options:'-O0+-mmcu%3Datmega16',source:'%0A%0A%0Avoid+foo(void)%0A%7B%0A++volatile+unsigned+char+k+%3D+2%3B%0Avolatile+unsigned+short++i+%3D+2%3B%0Avolatile+unsigned+int+j+%3D+2%3B%0A++++++++++k+%3D+i+*+j%3B%0A%7D')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3

Jaką masz wersję kompilatora?

edytowany 1x, ostatnio: alagner, 2016-06-19 20:19

Pozostało 580 znaków

Odpowiedz

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0