Atomarer Zugriff von 16 Bit Variable in 8 Bit Controller

OP #980460
Hallo,

wir entwickeln software fuer kritische anwendungen.

Heute habe ich in der abteilung eine diskussion bei einem CodeReview 
mitbekommen.

Inhalt des Reviews:

globale 16 bit variable in modul A
u16 u16_A;

in der RTC ISR wird dieser wert nun gegen null verglichen und 
dekrementiert:
if (u16_A > 0 )
   u16_A -= 1;

mein kollege hat nun angemerkt, dass bei einem 8 bit controller 
zugrifffe auf 16 bit variablen intern ja nicht mit einem assembler 
befehl geschehen können. Dies könnte unter umständen dazu führen, dass 
u16_A verä.... und schwups RTC Interrupt (ausgelesen)... ndert wird... 
und das dabei was schief gehen kann.

1. ist es natürlich unschön, so mit dieser globalen variable umzugehen

aber ist es wirklich so ein Problem? oder regelt der compiler solche 
zugriffe intern über abschalten der interrupts während eines solchen 
zugriffs??

würde mich wirklich interessieren.

COntroller ist übrigens der HC(S)08
compiler ist der coderWarrior

MFG
 und gute nacht ;-)
#980466
@ Sebastian B. (mircobolle)

>wir entwickeln software fuer kritische anwendungen.

Klorollenabwickler? ;-)

SCNR

>1. ist es natürlich unschön, so mit dieser globalen variable umzugehen

Nö, das passt, WENN . . .

>aber ist es wirklich so ein Problem?

Ja.

> oder regelt der compiler solche
>zugriffe intern über abschalten der interrupts während eines solchen
>zugriffs??

Nöö, da ist Handarbeit gefragt, siehe Interrupt.

MFG
Falk
Gast #980469
nein, musst du selbst machen.
Also vor Zugriff Interrupts abschalten - zumindest die, die die Variable 
verändern können.
Ist ein altes Problem, was oft übersehen wird, funktioniert ja 
normalerweise. Aber nur, weil die Wahrscheinlichkeit gering ist, dass 
genau zwischen den beiden Zugriffen was passiert (und dann ja noch ein 
Übertrag L->H stattfinden muss, ansonsten ist der Wert ja nur nicht ganz 
aktuell, aber nicht prinzipiell falsch). Irgendwann passiert es aber.
Dein Kollege hat absolut recht!
Gast #980501
globale 16 bit variable in modul A
u16 u16_A;

in der RTC ISR wird dieser wert nun gegen null verglichen und
dekrementiert:
if (u16_A > 0 )
   u16_A -= 1;

Dieser Code ist identisch mit

u16 u16_A;
if (u16_A)
   u16_A -= 1;

was an sich nicht schlimm ist, das schlimme daran ist,
daß u16_A nicht als volatile gekennzeichnet ist, denn wenn er als 
volatile
gekennzeichnet wäre, müsste der Compiler 2-Mal auf die Variable 
zugreifen,
und das Problem behebt sich von selbst, zumindest aus der Einsicht, die 
du uns gegeben hast.
Gast #980527
Da hab ich auch gleich mal eine Frage zu.

Gibt es auch ein Problem wenn ich 16/32/64.Bit variablen ausschließlich 
außerhalb von Interrups verwende.

@crazy horse (Gast)

Das Problem kann je nach interrupt selten oder auch SEHR oft auftreten. 
mir ist aufgefallen das solche "Zufälle" in einer CPU sehr 
wahrscheinlich sind.
Gast #980530
> Gibt es auch ein Problem wenn ich 16/32/64.Bit variablen ausschließlich
> außerhalb von Interrups verwende.
Nein, eigentlich nicht. Das einzige, was passieren kann, ist dass wenn 
du grad auf so ne Variable zugreifst, der Interrupt zwischen den 
Byte-Zugriffen auftritt. Aber das macht den Variablen im Normalfall nix.

Ralf
Gast #980532
>...der Interrupt zwischen den Byte-Zugriffen auftritt.
>Aber das macht den Variablen im Normalfall nix

Das passiert viel öfter als Du denkst.

Globale Variabeln die Du innerhalb und ausserhalb einer ISR verwendest 
müssen volatile sein, denn sonst wird der Optimiser die Werte mit 
grosser Warscheinlichkeit in Register halten, da er nicht wissen kann, 
dass diese von einer ISR benutzt werden!

Innerhalb einer ISR ist der Zugriff auf 16/32 Bit Variabeln sicher, da 
dann weitere Interupts gesperrt sind. (Es sei den Du lässt diese wieder 
explizit wieder zu)

Ausserhalb von ISRs führen solche Zugriffe zu Problemen, wenn die Werte 
solcher Variabeln innerhalb einer ISR verändert werden. Dann musst Du 
Interrupts unbedingt sperren, siehe folgendes Beispiel:
1
unsigend char sreg_cpy;
2
volatile unsigned long var32;
3
//------------------------------------
4
sreg_cpy=SREG;   // save SREG
5
cli();           // disable interrupts
6
var32 = var32+1; // save usaged of  var
7
SREG=sreg_cpy;   // restore SREG


MfG Peter
Gast #980539
Das beschriebene Problem tritt immer dann auf, wenn die Bitzahl einer 
Variablen größer als die Datenbusbreite des µC ist.

Achte hier auch auf Timerzugriffe  wenn 16Bit Timer verwendet werden.
Es ist durchaus möglich das ein Timerüberlauf zwischen den beiden 
Lesezugriffen erfolgt.

Eine Möglichkeit das zu umgehen ist folgenden:

1. Highbyte auslesen
2. Lowbyte auslesen
3. Highbyte nochmal lesen
4. Beide Highbytes vergleichen
5. Highbyte identisch ?
   a. Ja ==> fertig
   b. Nein ==> Sprung zu 1.

Nachteil: Benötigt mehr Laufzeit als Möglichkeit 2

2. Möglichkeit:

1. Timer stop
2. Highbyte und Lowbyte auslesen
3. Timer start

Nachteil: Timer steht für einige µsec


Auch hier gilt wie für alle anderen 16 Bit zugriffe auf einem 8 Bit µC:
Prüfen ob IRQ abgeschaltet werden müssen, in diesem Fall empfehlenswert.
Gast #980544
@Peter:
> Das passiert viel öfter als Du denkst.
Ja, das schon, aber er hatte danach gefragt, was passiert, wenn er die 
Variablen nur ausserhalb der ISRs verwendet.

Ralf
#980549
@ Ralph (Gast)

>Achte hier auch auf Timerzugriffe  wenn 16Bit Timer verwendet werden.
>Es ist durchaus möglich das ein Timerüberlauf zwischen den beiden
>Lesezugriffen erfolgt.

Schon, aber der AVR regelt das per Hardware Zwischenpuffer, siehe 
Datenblatt.
Andere uCs machen das ähnlich.

Die hier vorgeschlagenen Methoden sind nicht sinnvoll und notwendig.

MFg
Falk
#980550
Ralph wrote:
> Das beschriebene Problem tritt immer dann auf, wenn die Bitzahl einer
> Variablen größer als die Datenbusbreite des µC ist.

Nein!

Das Problem gibt es auch bei read/write-Zugriffen (++,--,|= usw.).
Oder wenn man eine Bitvariable zuweist.
Manche Architekturen (8051) können auch Bitvariablen atomar setzen.


Peter
Gast #980554
Noch ne weitere Anmerkung:

auch bei 16bit Controllern bei denen eine 16bit Zahl vom Speicher 
gelesen wird kann es zu 2Lesezyklen und damit veränderung während des 
lesens kommen. Sobald im Speichermapping die 16bit Zahl auf einer 
ungeraden Adresse beginnt gibt es das gleiche Problem.

Wie kommst es dazu?
- einfach in einer Struktur Bytes definieren
    struct ( char x;
             int  y;
           )
- generell im mapping 8,16,32 bit variablen nicht getrennt verwalten




JL
Gast #980557
Mal eine Frage...
wenn die ISR irgendwie an den Registern rumfummelt muss diese auch dafür 
sorgen das die auch wieder hergestellt werden, oder etwa nicht?
OP #980561
Falk Brunner wrote:
> @ Sebastian B. (mircobolle)
>
>>wir entwickeln software fuer kritische anwendungen.
>
> Klorollenabwickler? ;-)
>
> SCNR
-> Kein Problem ;-)

kritischer wäre eine Regelung, die den Füllstand einer mobilen 
Camping-Toilette überwachen soll. Ein Grenzwert-Fehler könnte hier zu 
fatalen Folgen führen... ;-) so viel zum Theme Kritikalität

>Schon, aber der AVR regelt das per Hardware Zwischenpuffer, siehe
>Datenblatt.
>Andere uCs machen das ähnlich.

Ich dachte mir eben auch deshalb, dass evtl. 16 Bit variable in solche 
register zwischen gepuffert werden.

aber da lag ich wohl falsch.

Ich exerziere mal kurz einen Fehler-Fall durch:

Ich schreibe in main() auf die globale Variable

volatile u16 v_u16_A;


main(){

    while(1){
    /* ... */
    if (v_u16_B > 0)
       v_u16_A = 0;
    /* ... */

    }

}


in der RTC ISR wuerde nun sowas passieren wie:

rtc_isr (){
   if (v_u16_A > 0)
      v_u16_A -= 1;
}

die ISR laeuft nun mit einem Takt von 2 ms...

Startwert:
v_u16_A = 10;

zuerst:
main-Zugriff

zwischen HIGH und LOW Byte -> INTERRUPT
v_u16_A[HIGH] = 0x00;
...
v_u16_A[LOW]  = 0x0A;

ISR RTC:
...
v_u16_A -= 1; /* --> 9 */

zurueck zu main:
v_u16_A[LOW]  = 0x00;



In diesme Fallw wuerde der ISR Zugriff ja nicht "stoeren"...
ich mein klar ist es ein Fehler und natuerlich ist es unschoen, aber in 
diesem konkreten Fall, sehe ich jetzt keine weiteren Probleme, da main 
die Variable ja noch ueberbuegeln kann..

Wenn der Interrupt direkt vorher auftritt:
alles prima: dekrementieren... pruefen/auf null setzen

wenn der interrupt danach auftritt:
auch alles prima: wert auf null setzen... pruefen/...

MFG
Gast #980960
@Ralf
>@Peter:
>> Das passiert viel öfter als Du denkst.
>Ja, das schon, aber er hatte danach gefragt, was passiert, wenn er die
>Variablen nur ausserhalb der ISRs verwendet.

Hab ich ja geschrieben: Es ist nur ein Problem, wenn globale Variabeln 
innerhalb einer ISR modifiziert werden, sonst nicht!
#980981
@Peter (Gast)

>Hab ich ja geschrieben: Es ist nur ein Problem, wenn globale Variabeln
>innerhalb einer ISR modifiziert werden, sonst nicht!

Es ist auch ein Problem, wenn sie "nur" gelesen werden.
Man kann glaub ich sagen, dass sobald Variablen sowohl im Haputprogramm 
als auch ISRs benutzt werden, egal ob lesend oder schreibend, atomare 
Operatioenen und volatile Definitionen notwendig sind.

MFG
Falk

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 18 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.