RTOS blokowanie przerwań

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" ?

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?

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ć ?

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.

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.

0

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

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...

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.

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 ?

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 ?

0

Avr-gcc zainstalowany z pakietu WinAvr 20100110
Wszystko mam podpięte pod Eclipse'a wraz z wtyczką i AvrDude.

Swoją drogą - jak sprawdzić wersję kompilatora ?

Słuchaj nic nie ściemniam , przecież z palca nie piszę kodu assemblerowego. Takie coś mi wypluwa eclipse do pliku .lss .
Czy możesz ktoś mi odpowiedzieć na moje pytanko dot. tych trzech linii kodu assemblerowego, wie ktoś po co kompilator to wsadził tam ?

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