Forum: Mikrocontroller und Digitale Elektronik Atomarer Zugriff von 16 Bit Variable in 8 Bit Controller


von Sebastian B. (mircobolle)


Lesenswert?

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 ;-)

von Falk B. (falk)


Lesenswert?

@ 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

von crazy horse (Gast)


Lesenswert?

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!

von gast (Gast)


Lesenswert?

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.

von Andreas W. (Gast)


Lesenswert?

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.

von Ralf (Gast)


Lesenswert?

> 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

von Peter (Gast)


Lesenswert?

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

von Ralph (Gast)


Lesenswert?

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.

von Ralf (Gast)


Lesenswert?

@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

von Falk B. (falk)


Lesenswert?

@ 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

von Peter D. (peda)


Lesenswert?

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

von jl (Gast)


Lesenswert?

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

von Gast (Gast)


Lesenswert?

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?

von Sebastian B. (mircobolle)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@ Gast (Gast)

>wenn die ISR irgendwie an den Registern rumfummelt muss diese auch dafür
>sorgen das die auch wieder hergestellt werden, oder etwa nicht?

Ja, siehe Interrupt.

von Peter (Gast)


Lesenswert?

@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!

von Falk B. (falk)


Lesenswert?

@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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.