Sub accesul atomic, vom înțelege un astfel de apel (citit, modificat, scrie sau secvența) într-un registru variabil sau periferic, ceea ce nu va conduce la un conflict atunci când o întrerupere sau prioritizare a sarcinii apare în sistemele RTOS în timpul operației. Un conflict poate apărea atunci când o întrerupere sau o altă sarcină accesează aceeași resursă.
Un exemplu simplu: trebuie să inversați ieșirea controlerului RA0:
Din punctul de vedere al sintaxei și al logicii, totul este corect, dar să ne uităm la codul care va rezulta din compilație
În exemplul de mai sus, valoarea curentă a registrului LATA este încărcată în registrul kernel-ului, modificată și apoi salvată înapoi în registrul LATA. Acum, imaginați-vă că după finalizarea instrucțiunilor
o valoare care nu ia în considerare setarea de biți din întrerupere va fi scrisă în port. Tija de grafit nu a scăzut. Boom!
Ca rezultat, avem un conflict de acces la resursă și, ca urmare, un program ineficient. Dacă credeți că această situație este depășită, faceți clic pe link. Aceasta este o întrebare reală adresată de o persoană reală care a dat peste problema accesului atomic la periferie imediat după începerea dezvoltării RTOS TNKernel.
Există mai multe opțiuni pentru rezolvarea problemei. Fiecare dintre ele are avantajele și dezavantajele sale.
Modul cel mai simplu și cel mai evident: pentru a vă asigura că secvența de instrucțiuni nu este încălcată, trebuie doar să excludeți chiar posibilitatea de a rula un cod concurențial. În acest caz, operația de inversare a biților va fi efectuată în mod atomic. Cu toate acestea, această metodă are multe deficiențe:
Măriți cantitatea de cod datorită instrucțiunilor care interzic și permit întreruperi.
Reducerea vitezei de executare a codului din aceleași motive.
Cu această metodă de implementare a accesului atomic, este imposibil să se furnizeze o întârziere deterministă a intrării la interrupt-jitter va apărea. În unele sisteme acest lucru este inacceptabil.
Această soluție nu poate fi utilizată în sistemele cu RTOS preemptiv (există metode proprii).
Dacă platforma țintă include un controler flexibil de întrerupere, efectele secundare ale acestei metode pot fi destul de puternice, deoarece sunt necesare mai multe acțiuni pentru a dezactiva întreruperile. De exemplu, pentru PIC24 / dsPIC, trebuie să salvați prioritatea actuală a kernel-ului în variabila temporară (pe stivă), setați prioritatea kernel-ului la maximum și după ce operația de acces atomic este executată, restabiliți prioritatea din variabila temporară. Aceasta este singura modalitate corectă de a dezactiva întreruperile atunci când dezvoltați software în limba C (utilizarea instrucțiunii disi nu este recomandată).
Această metodă poate fi utilizată atunci când se utilizează un RTOS preemptiv. O secțiune critică face parte din codul în care se interzice comutarea contextului. Cel mai adesea aceasta înseamnă dezactivarea întreruperilor și (eventual) efectuarea de acțiuni suplimentare asupra variabilelor interne ale programatorului.
Secțiunea critică are aceleași dezavantaje ca metoda anterioară. În plus, utilizarea secțiunilor critice nu este încurajată, deoarece în acest caz interferați cu planificatorul și încălcați principiile funcționării RTOS. Dacă accesul la resursă poate fi alocat unei bucăți relativ mari de cod, atunci este mai rezonabil să folosiți mutexuri.
Mutex este un obiect RTOS destinat implementării accesului competitiv la o resursă comună pentru sarcini.
Dacă nu implementați mecanismul mutex în RTOS, puteți utiliza semafoare binare, dar în acest caz poate să apară o inversare prioritară - o situație neplăcută în care o sarcină cu prioritate mai mare nu poate accesa resursa.
Utilizarea mutexelor este metoda cea mai corectă de a asigura accesul la resursele partajate. Cu toate acestea, atunci când servesc periferice, această metodă poate fi foarte costisitoare în ceea ce privește volumul și, cel mai important, viteza de execuție a codului.
Limitați mutexurile la o parte relativ mare a codului, dar nu accesați câmpul de structură sau bitul de port.
Pentru metodele hardware de furnizare a accesului atomic, puteți să consultați instrucțiunea disi a controlorilor Microchip pe 16 biți. Această instrucțiune interzice întreruperile printr-un anumit număr de cicluri de comandă.
Am examinat problema generală a accesului competitiv la resursele programelor. Cu toate acestea, există și un caz special al acestei probleme - accesul la câmpurile de biți ale structurii.
Când accesați aceste câmpuri de biți, compilatorul poate genera o instrucțiune atomică:
Dar lucrurile se vor agrava mult atunci când încercați să scrieți valoarea unei variabile în câmpul de structură:
Probleme pot apărea, de asemenea, atunci când accesați un câmp de biți care este mai mare de 1 bit:
Modul de a rezolva problema este să utilizați instrucțiunea xor. care oferă acces atomic în orice caz. chiar dacă doriți să modificați un câmp de biți mari.
Să luăm în considerare un exemplu: scrierea la cei patru biți inferiori ai registrului TRISB cu valoarea 0x000A.
prima instrucțiune încarcă valoarea TRISB în registrul W0;
A doua instrucțiune modifică atomic și în siguranță cei patru biți cei mai puțin semnificativi ai registrului W0;
a treia instrucțiune suprapune o mască pe registrul W0, alocând 4 biți semnificativi
ultima instrucțiune este atomică și în siguranță (fără a atinge biții rămași!) modifică TRISB; TRISB = 0xFFFA;
Chiar dacă se produce o întrerupere după executarea primei instrucțiuni, care va schimba valoarea TRISB (firește nu cel puțin 4 biți), operația va fi efectuată corect. Codul de mai sus este atomic.
În opinia mea umilă, aceste macrocomenzi sunt cele mai înalte acrobații. Ei folosesc o mulțime de soluții interesante pe care le voi discuta mai jos.
BFAR () macro fix
Instrucțiunile pentru întreținerea unităților biți se înlocuiesc cu cele de asamblare, deci pentru un anumit nivel de optimizare, codul cel mai puțin neoptimal
Adăugat macrocomenzi BFAM (). BFAMI (). BFAMD ()
Sunt destinate acelorasi, pentru ce si BFAR (). Dar biții afectați de operație nu sunt respectați de o gamă, ci de o mască. Descrierea detaliată va fi puțin mai târziu
Corecție de bug în macro BFAR ()
Dacă ați setat opțiunea de compilare la nivel granular-scalar (pentru a avea variabile scalare în zona RAM îndepărtată), a apărut o eroare la utilizarea unei macrocomenzi BFAR () cu o variabilă arbitrară (fără atributul SFR).
BFA macro fix ()
Când utilizați macrocomanda BFA (), a fost emis un avertisment cu privire la conversia necondiționată a unei constante mari într-un int nesignificat
Adăugat macro BFARD (). destinate accesului direct la orice variabilă scalară în RAM.
Ordinea transferului parametrilor este modificată
Parametrul care indică tipul de operație din macro este trecut mai întâi
Adaugat macro BFARI ()
Macro-ul BFARI () oferă acces la structură printr-un pointer
Au fost adăugate operații noi pentru toate macrocomenzile: BFA_SET și BFA_CLR
Aceste operațiuni pot fi folosite pentru a seta sau a șterge biți prin mască în câmpul de biți. Masca poate fi trecută ca o constantă sau ca variabilă.
BFA_IV este înlocuit cu BFA_INV. Operația de inversiune inversează acum biții de-a lungul măștii transmise.
Prima versiune. Doar pentru compilatorul Microchip C30
Arhiva conține fișierul header bfa.h. care include patru macro-uri care implementează accesul atomic la câmpul de structură sau la orice variabilă scalară. Una dintre macrocomenzi este destinată lucrului cu structuri, ale căror nume corespund regulilor Microchip C30 pentru registrele periferice. Restul macrocomenzilor pot fi utilizate cu orice structură sau variabilă de dimensiuni care nu sunt mai mari decât cuvântul mașinii (int).
Pentru a utiliza macrocomenzile, este necesar și suficient să conectați fișierul bfa.h la modul și să activați optimizarea nu mai mică de -01.
Accesul atomic la câmpurile numite ale registrelor periferice PIC24 / dsPIC
Accesul atomic la câmpurile de biți ale registrelor și variabilelor periferice. Câmpul de biți este indicat ca un interval numeric. Variabila trebuie să fie localizată în SPAȚIUL DE DATE NEAR (primele 8 r<ОЗУ)
Accesul atomic la câmpurile de biți ale registrelor periferice și ale variabilelor prin pointer. Câmpul de biți este indicat ca un interval numeric
Accesul atomic la câmpurile de biți ale registrelor și variabilelor periferice. Câmpul de biți este indicat ca un interval numeric. Variabila poate fi localizată oriunde în RAM (spațiu de date NEAR sau FAR)
Macro-ul oferă acces atomic la câmpurile numite ale structurilor de registru periferice ale microcontrolerelor PIC24 / dsPIC.
inversarea biților în câmpul de biți prin mască
reg_name este numele registrului periferic accesat, de exemplu, PORTA. TRISIB. CMCON și altele asemenea. field_name numele câmpului de biți din structura registru (consultați documentația pentru microcontroler și fișierul antet C30 pentru acest microcontroler). De exemplu, pentru registrul IPC0, acesta poate fi T1IP. opțional, este utilizat numai dacă com = [BFA_WR, BFA_SET, BFA_CLR, BFA_INV]. Dacă comm = BFA_RD, parametrul nu este specificat. Dacă comm = BFA_WR. parametrul indică valoarea care este scrisă în câmpul de biți. În alte cazuri, parametrul trebuie să fie egal cu bitul mască. Parametrul poate fi o variabilă.
Macro-ul oferă acces atomic la câmpul de biți al oricărei structuri sau variabile care se află în zona NEAR DATA SPACE (primele 8 KB de RAM). Câmpul de biți este indicat ca domeniu (biți de ordin mic / bit de înaltă ordine).
com tipul de acces:
inversarea biților în câmpul de biți prin mască
reg_name este numele registrului care este accesat, de exemplu, PORTA. TRISIB. CMCON și altele asemenea. Poate fi orice variabila de program care este in zona de memorie apropiata (primele 8 kB) cel mai mic bit al campului de structura, de la 0 la 15 superioara cel mai semnificativ bit al campului de structura, de la 0 la 15, superior> = mai mic. opțional, este utilizat numai dacă com = [BFA_WR, BFA_SET, BFA_CLR, BFA_INV]. Dacă comm = BFA_RD, parametrul nu este specificat. Dacă comm = BFA_WR. parametrul indică valoarea care este scrisă în câmpul de biți. În alte cazuri, parametrul trebuie să fie egal cu bitul mască. Parametrul poate fi o variabilă.
Macro-ul oferă accesul atomic la câmpul de biți al oricărei structuri sau variabile de pointer. Câmpul de biți este indicat ca domeniu (biți de ordin mic / bit de înaltă ordine). Variabila poate fi în spațiul de date NEAR și FAR DATA.
com tipul de acces:
inversarea biților în câmpul de biți prin mască
pt indicatorul la registrul periferic sau orice variabilă. Macroul ia automat un pointer la un tip int *. cel mai mic bit cel mai puțin semnificativ al câmpului de structură, de la 0 la 15 biți cel mai semnificativ din câmpul structurii, de la 0 la 15, superior> = mai mic. opțional, este utilizat numai dacă com = [BFA_WR, BFA_SET, BFA_CLR, BFA_INV]. Dacă comm = BFA_RD, parametrul nu este specificat. Dacă comm = BFA_WR. parametrul indică valoarea care este scrisă în câmpul de biți. În alte cazuri, parametrul trebuie să fie egal cu bitul mască. Parametrul poate fi o variabilă.
Macro-ul oferă acces atomic la câmpul de biți al oricărei structuri sau variabile care se află în orice zonă RAM (NEAR sau FAR). Câmpul de biți este indicat ca domeniu (biți de ordin mic / bit de înaltă ordine).
com tipul de acces:
inversarea biților în câmpul de biți prin mască
val este numele registrului care va fi accesat, de exemplu, PORTA. TRISIB. CMCON și altele asemenea. Poate fi orice variabilă de program scalar. cel mai mic bit cel mai puțin semnificativ al câmpului de structură, de la 0 la 15 biți cel mai semnificativ din câmpul structurii, de la 0 la 15, superior> = mai mic. opțional, este utilizat numai dacă com = [BFA_WR, BFA_SET, BFA_CLR, BFA_INV]. Dacă comm = BFA_RD, parametrul nu este specificat. Dacă comm = BFA_WR. parametrul indică valoarea care este scrisă în câmpul de biți. În alte cazuri, parametrul trebuie să fie egal cu bitul mască. Parametrul poate fi o variabilă.
Macrourile de mai sus verifică parametrul transmis com. indicând metoda de acces la câmpul de structură. Parametrii rămași nu sunt bifați. Următoarele valori admise sunt definite pentru com:
Controlul transferului de parametri către macro este făcut într-un mod interesant: anteturile declară unic pentru fiecare tip de parametru:
Dacă parametrul trecut la macro este diferit de cel specificat BFA_WR. BFA_RD și BFA_IV. compilatorul va genera o eroare:
Folosind declarații condiționate. a permis să implementeze cu o singură macrocomandă atât execuția operației, cât și valoarea returnată. Dacă nu intri în detaliile implementării, toate macrocomenzile arată astfel:
Dacă parametrul comm (tipul de acces la structură) este egal cu BFA_RD. atunci macroul generează următoarea expresie:
Astfel, scrierea
după lucrarea preprocesorului, primim o sarcină simplă:
Desigur, utilizarea directă a macrocomenzilor BFA () și BFAR () nu este foarte evidentă. Cu toate acestea, aceasta este una dintre metodele:
Dacă registrul este folosit în aplicația dvs. numai pentru scriere, puteți defini următoarea macrocomandă:
și apoi utilizați-l deja:
Dacă câmpul de structură este folosit atât pentru scriere cât și pentru citire, puteți defini următoarea macro cu un număr variabil de parametri:
Utilizați această macrocomandă după cum urmează:
De foarte multe ori este necesar să se obțină accesul atomic la câmpul neanunțat al structurii - de exemplu, pentru a seta o anumită valoare pe mai multe ieșiri ale regulatorului. Puteți utiliza macrocomanda BFAR () pentru a face acest lucru.
Mai mult, este evident:
Utilizarea acestor macrocomenzi este obligatorie pentru oricine dorește să scrie cod rapid și securizat pentru microcontrolerele PIC24 / dsPIC folosind compilatorul C30.
Această abordare poate fi implementată pentru orice altă arhitectură și un alt compilator. Pentru aceasta, este necesar ca:
compilatorul a avut un asamblor flexibil in-line (de exemplu, ca în gcc)
arhitectura a avut mecanisme pentru acces direct la memorie
Aceasta din urmă înseamnă că trebuie îndeplinită una dintre următoarele două condiții:
Un set de instrucțiuni trebuie să includă o instrucțiune xor cu acces direct la zona de interes
Arhitectura trebuie să aibă mecanisme speciale pentru acces direct. Un exemplu este zona de bandă de biți a registrelor Cortex-M3 sau SET / CLR / INV pentru întreaga periferie a PIC32.
Principalele avantaje ale macrocomenzilor BFA () și BFAR () sunt implementarea accesului atomic la resursele partajate. Desigur, acest lucru nu înseamnă că în întrerupere și în codul principal este posibil să se miște același picior de controler fără durere. Dar accesul la diferite biți din port este sigur.
Prin utilizarea macro-uri BFA () și BFAR () nu este nevoie pentru a dezactiva întrerupe sau punerea în aplicare a secțiunii critice. Aceasta vă permite să determinați timpul de intrare în întrerupere, să reduceți cantitatea de cod ocupată și viteza de operare.
Mai mult, atunci când se utilizează macro-uri care accesează câmpurile de structură, cantitatea de cod și timpul de execuție sunt reduse în comparație cu accesul direct: