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 ;-)
@ 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
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!
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.
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.
> 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
>...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
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.
@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
@ 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
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
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
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?
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 (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.
@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!
@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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.