Forum: Compiler & IDEs Interrupt / SREG / Priorität


von Andi_T T. (andi_t)


Lesenswert?

Hallo zusammen,
ich habe zu den Interrupts (ATmega32) zwei Fragen.

1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine 
immer selbst sichern und vor Beendigung wieder zurückspeichern, 
ansonsten kann es je nach Operation in der Interrupt-Routine und 
Änderung der SREG-Flags beim Rücksprung zum Bruch führen.

Besteht aber auch die Gefahr, dass Operationen mit 16 bit oder 32 bit 
Variablen, die NICHT in der Interrupt-Routine verwendet und verändert 
werden, korrupt werden, weil ein Interrupt zw. den Bytes auftritt, d.h. 
muss hier das Interrupt Flag gesperrt werden oder ist dies überflüssig,

z.B.:
void main (void)
  uint16_t (oder uint32_t) sum, buffer;
  uint8_t save_sreg;

  save_sreg = SREG;
  cli();
  …
  buffer = (((sum * 5)/10) - (sum/100) - (sum/1000));
  ….
  SREG = save_sreg;

2.Die Interrupt-Priorität ist im ATmega fest vergeben, wenn aber das 
global I-Flag im Interrupt sofort wieder freigeben wird, könnte 
theoretisch auch ein Interrupt mit niedrigerer Priorität dazwischen 
ausgeführt werden oder liege ich hier falsch, z.B.:

ISR (TIMER0_OVF_vect)
  uint8_t sreg_temp;
  sreg_temp = SREG;
  sei();
  ….. // z.B. SPI Interrupt (geringere Priorität)
  SREG = sreg_temp;

Vielen Dank für eine Rückmeldung.
Andreas

von (prx) A. K. (prx)


Lesenswert?

1. Was die ISR nicht ändert, muss gegenüber dieser ISR nicht abgesichert 
werden. Ausnahme: Man schreibt die ISR in Assembler und vergisst, alle 
verwendeten Register zu sichern.

2. Die "Prioritäten" bei AVRs (ohne XMega) beziehen sich nur auf 
gleichzeitig beim Vektorzugriff anstehende Interrupts. Gibt man in der 
ISR Interrupts frei, kommt jeder Interrupt durch. Das unterscheidet 
AVR von Architekturen mit komplexem über Prioritäten verschachtelbaren 
Interruptsystemen wie den ARM Cortex M. Aber auch von 8051 und 8-Bits 
PICs mit zwei Ebenen.

: Bearbeitet durch User
von Andi_T T. (andi_t)


Lesenswert?

Vielen Dank für die Antwort, nur nochmals zur Sicherheit.

zu Frage 1: SREG in ISR sichern ist erforderlich, dass Sperren von 
Interrupts, solange die Variable nicht im Interrupt bearbeitet wird ist 
nicht erforderlich.

zu Frage 2: d.h. der SPI Interrupt kann innerhalb der des 
Timer-Interrupts ausgeführt werden und muss nicht warten bis der Timer 
ISR abgearbeitet wurde, d.h. er wäre für die SPI Kommunikation 
unterbrechbar.

von Rolf M. (rmagnus)


Lesenswert?

Andi_T T. schrieb:
> 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine
> immer selbst sichern und vor Beendigung wieder zurückspeichern,
> ansonsten kann es je nach Operation in der Interrupt-Routine und
> Änderung der SREG-Flags beim Rücksprung zum Bruch führen.

Ja, das SREG musst du sichern.

> Besteht aber auch die Gefahr, dass Operationen mit 16 bit oder 32 bit
> Variablen, die NICHT in der Interrupt-Routine verwendet und verändert
> werden, korrupt werden, weil ein Interrupt zw. den Bytes auftritt, d.h.
> muss hier das Interrupt Flag gesperrt werden oder ist dies überflüssig,

Nein, wenn die ISR sie nicht verwendet, besteht da keine Gefahr.

> 2.Die Interrupt-Priorität ist im ATmega fest vergeben, wenn aber das
> global I-Flag im Interrupt sofort wieder freigeben wird, könnte
> theoretisch auch ein Interrupt mit niedrigerer Priorität dazwischen
> ausgeführt werden oder liege ich hier falsch, z.B.:

Nein, da liegst du richtig. Wenn das I-Flag, das Enable-Flag des 
Interrupts und das Interrupt-Flag selbst gesetzt sind, wird in der 
nächsten Instruktion die entsprechende ISR angesprungen, egal was da 
sonst gerade läuft.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andi_T T. schrieb:

> zu Frage 1: SREG in ISR sichern ist erforderlich,

Nur, wenn deine ISR auch Operationen ausführt, die das SREG ändern.

Lässt deine ISR beispielsweise nur via OUT eine LED aufleuchten, ist das 
nicht der Fall. Das dürfte allerdings nahezu der einzige praktikable 
Fall sein, bei dem das so ist, d.h. jede komplexere ISR wird das 
benötigen. Da du für das Sichern des SREG selbst noch ein CPU-Register 
brauchst, musst du das natürlich vorher ebenfalls sichern.

> dass Sperren von
> Interrupts, solange die Variable nicht im Interrupt bearbeitet wird ist
> nicht erforderlich.

"die Variable"?

Falls es deine Frage ist, ob die Interrupts beim Eintritt in die ISR 
automatisch gesperrt worden sind: ja, dem ist so. Ansonsten könnte dir 
ja schon während der Präambel-Abarbeitung der ISR der nächste Interrupt 
reinknalllen.

Der übliche Weg, sie nach der ISR wieder zu gestatten, ist via RETI.

> zu Frage 2: d.h. der SPI Interrupt kann innerhalb der des
> Timer-Interrupts ausgeführt werden und muss nicht warten bis der Timer
> ISR abgearbeitet wurde, d.h. er wäre für die SPI Kommunikation
> unterbrechbar.

Wenn du innerhalb der ISR die Interrupts manuell wieder frei gibst, kann 
dir jeder Interrupt ab diesem Moment deine ISR unterbrechen – 
inklusive des eigenen (kann bspw. bei UARTs passieren).

Daher lässt man die Interrupts beim AVR üblicherweise in der ISR 
gesperrt und hält lieber die ISR kurz.

von Gcc (Gast)


Lesenswert?

Wenn du, wie oben zu sehen, die ISR in C schreibst, dann kümmert sich 
der Compiler um das SREG, in den neueren GCC-Versionen (ab 9, WIMRE) 
läst er das auch weg, wenn die ISR das SREG nicht braucht.
Einzig die vorzeitige Freigabe weiterer Interrupts obliegt dem 
Programmierer. Der sollte aber dann auch wissen, was er tut.
Variablen, die von der ISR verändert und in einer "Main-Loop" gelesen 
werden, müssen "volatile" sein, sonst erkennt der Compiler nämlich 
fälschlicherweise, daß sich die Variable "nie" ändert. Aber bitte nur 
die. "volatile" für alles "zur Sicherheit" deutet nur auf ein 
fehlerhaftes Programm hin.

von Peter D. (peda)


Lesenswert?

Andi_T T. schrieb:
> 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine
> immer selbst sichern und vor Beendigung wieder zurückspeichern,

In Assembler ja, in C nein.
Der Compiler weiß alleine, was zerstört werden könnte und daher 
gesichert werden muß. Der Compiler sorgt quasi dafür, daß der 
Interrupthandler für die Mainloop komplett unsichtbar bleibt. Es ist nur 
eben deutlich mehr Zeit vergangen, als für die letzte Instruktion nötig 
gewesen wäre. D.h. Delayloops können sich durch den Interrupt 
verlängern.


Andi_T T. schrieb:
> ISR (TIMER0_OVF_vect)
>   uint8_t sreg_temp;
>   sreg_temp = SREG;
>   sei();

Das sei(); sollte man in Interrupts extrem vorsichtig benutzen. Der AVR 
kennt keine Interruptlevel, d.h. absolut jeder enablete Interrupt kann 
dann reingrätschen, sogar der, der gerade in Behandlung ist.

von Falk B. (falk)


Lesenswert?

Andi_T T. schrieb:

> 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine
> immer selbst sichern und vor Beendigung wieder zurückspeichern,

Nur in Assembler, nicht in C.

> Besteht aber auch die Gefahr, dass Operationen mit 16 bit oder 32 bit
> Variablen, die NICHT in der Interrupt-Routine verwendet und verändert
> werden, korrupt werden, weil ein Interrupt zw. den Bytes auftritt, d.h.
> muss hier das Interrupt Flag gesperrt werden oder ist dies überflüssig,

Dein Problem nennt man atomaren Zugrifft. Wie man den richtig macht, 
steht hier.

https://www.mikrocontroller.net/articles/Interrupt#Atomarer_Datenzugriff

> 2.Die Interrupt-Priorität ist im ATmega fest vergeben, wenn aber das
> global I-Flag im Interrupt sofort wieder freigeben wird, könnte
> theoretisch auch ein Interrupt mit niedrigerer Priorität dazwischen
> ausgeführt werden oder liege ich hier falsch, z.B.:

Ja, man kann per Software verschachtelte Interrupts auf dem AVR machen, 
wenn man weiß was man tut.

von (prx) A. K. (prx)


Lesenswert?

Falk B. schrieb:
> Dein Problem nennt man atomaren Zugrifft. Wie man den richtig macht,
> steht hier.

Er hatte allerdings nach Auswirkungen der ISR auf Variablen gefragt, die 
in der ISR nicht angesprochen werden. Da ist atomarer Zugriff unnötig.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

(prx) A. K. schrieb:
> Er hatte allerdings nach Auswirkungen der ISR auf Variablen gefragt, die
> in der ISR nicht angesprochen werden. Da ist atomarer Zugriff unnötig.

Das ist eine Tautologie. Aber trotzdem schön, daß wir drüber gesprochen 
haben . . .

von Maxe (Gast)


Lesenswert?

Andi_T T. schrieb:
> 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine
> immer selbst sichern und vor Beendigung wieder zurückspeichern,
> ansonsten kann es je nach Operation in der Interrupt-Routine und
> Änderung der SREG-Flags beim Rücksprung zum Bruch führen.

Das SREG hat auf den Rücksprung keine Auswirkung. Der Compiler sichert 
das SREG, um u.A. folgendes zu verhindern:

> Besteht aber auch die Gefahr, dass Operationen mit 16 bit oder 32 bit
> Variablen, die NICHT in der Interrupt-Routine verwendet und verändert
> werden, korrupt werden, weil ein Interrupt zw. den Bytes auftritt, d.h.
> muss hier das Interrupt Flag gesperrt werden oder ist dies überflüssig,

Ist also überflüssig.

von Andi_T T. (andi_t)


Angehängte Dateien:

Lesenswert?

Gcc schrieb:
> Wenn du, wie oben zu sehen, die ISR in C schreibst, dann kümmert sich
> der Compiler um das SREG, in den neueren GCC-Versionen (ab 9, WIMRE)
> läst er das auch weg, wenn die ISR das SREG nicht braucht.

Vielen Dank für die Unterstützung,
im Disassembly sieht man, dass das SREG automatisch gesichert wird und 
nicht händisch nochmals durchgeführt werden muss.

GNU Compiler Invoking: AVR/GNU C Compiler : 5.4.0
gcc version 5.4.0 (AVR_8_bit_GNU_Toolchain_3.6.2_1778)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andi_T T. schrieb:
> gcc version 5.4.0

Der ist übrigens uuuuuuralt inzwischen (3. Juni 2016).

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Andi_T T. schrieb:
>> gcc version 5.4.0
>
> Der ist übrigens uuuuuuralt inzwischen (3. Juni 2016).

Das haben auch die Versionen vorher schon alle so gemacht. WinAVR in 
seiner letzten Version von 2010 mit gcc 3.x dürfte ja heute noch hier 
und da im Einsatz sein, noch ältere findet man wohl eher selten.
Aber schön, daß das auch in Jahre 2021 immer noch mal diskutiert wird.

Oliver

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Andi_T T. schrieb:

> 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine
> immer selbst sichern und vor Beendigung wieder zurückspeichern,
> ansonsten kann es je nach Operation in der Interrupt-Routine und
> Änderung der SREG-Flags beim Rücksprung zum Bruch führen.

Nein. Sichern und Wiederherstellen muss man SREG nur dann, wenn man in 
der ISR irgendwas an den Flags (AUSSER dem I-Flag) ändert.

Das I-Flag wird automatisch durch die Hardware vor dem Eintritt in die 
ISR gelöscht und durch das reti am Ende der ISR genauso automatisch auch 
wieder gesetzt, darum braucht man sich also normalerweise überhaupt 
nicht zu kümmern, nur um den Rest der Flags.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
>> Der ist übrigens uuuuuuralt inzwischen (3. Juni 2016).
>
> Das haben auch die Versionen vorher schon alle so gemacht.

Ja, sicher.

Aktuelle sind aber etwas intelligenter beim Generieren der ISR-Umgebung:
1
#include <avr/interrupt.h>
2
#include <avr/io.h>
3
4
ISR(INT0_vect)
5
{
6
        PIND = 1;
7
}
1
$ avr-gcc -Os -c -mmcu=atmega328 isrtest.c
2
$ avr-objdump -d isrtest.o
3
4
isrtest.o:     file format elf32-avr
5
6
7
Disassembly of section .text:
8
9
00000000 <__vector_1>:
10
   0:   8f 93           push    r24
11
   2:   81 e0           ldi     r24, 0x01       ; 1
12
   4:   89 b9           out     0x09, r24       ; 9
13
   6:   8f 91           pop     r24
14
   8:   18 95           reti

: Wiederhergestellt durch Moderator
von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Aktuelle sind aber etwas intelligenter beim Generieren der ISR-Umgebung:

Es wäre ja auch schlimm, wenn zig Compilergenerationen nicht doch irgend 
eine Verbesserung gebracht hätten.  Den kleinen Codegrößen vom gcc 3 
weinen aber heute noch viele nach ;)

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Den kleinen Codegrößen vom gcc 3 weinen aber heute noch viele nach

Ich nicht, denn ich denke, dass die nur gefühlt "klein" waren.

Sicher wirst du immer pathologische Fälle finden, aber im Großen und 
Ganzen sind vor allem durch Johanns Arbeit (ich glaube, es war ab GCC 
4.9) die Optimierungen für AVR deutlich besser geworden.

von LostInMusic (Gast)


Lesenswert?

Bei ISRn mit der Besonderheit, dass das SREG darin nicht verändert 
(also geschrieben) wird, muss man dieses auch nicht Sichern und 
Wiederherstellen.

Der dazu gewissermaßen entgegengesetzte Grenzfall wäre ein Programm, in 
dessen Nicht-ISR-Code ("Main"-Ebene) das SREG nirgendwo ausgewertet 
(also gelesen) wird. Meistens ist die Main-Schleife dann einfach leer 
("Loop: rjmp Loop"), d. h. das Programm läuft komplett in ISRn. Manche 
Leute finden Gefallen daran, das so zu machen, und unter bestimmten 
Bedingungen ist das auch völlig OK. Auch in diesem Fall kann man auf das 
Sichern und Wiederherstellen des SREG in den ISRn verzichten (solange 
man nicht mit verschachtelten Interrupts operiert).

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Oliver S. schrieb:
>> Den kleinen Codegrößen vom gcc 3 weinen aber heute noch viele nach
>
> Ich nicht, denn ich denke, dass die nur gefühlt "klein" waren.
>
> Sicher wirst du immer pathologische Fälle finden, aber im Großen und
> Ganzen sind vor allem durch Johanns Arbeit (ich glaube, es war ab GCC
> 4.9) die Optimierungen für AVR deutlich besser geworden.

Johanns Arbeit ist unbestritten wertvoll, und ich weine dem 3er gcc auch 
nicht nach. Den Codegrößenvergleich müsste man aber doch mal richtig 
machen.

Leider ging’s damit nach Johanns Ausscheiden ja wild hin und her, aber 
vor allem wieder rauf.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Den Codegrößenvergleich müsste man aber doch mal richtig machen.

Würde voraussetzen, den alten Compiler überhaupt noch rumliegen zu 
haben, außerdem eine Version der avr-libc, die damit compiliert ist.

Interessant wäre mal der Vergleich mit Clang, aber dazu habe ich mich 
auch noch nicht aufraffen können.

von 0er (Gast)


Lesenswert?

Andi_T T. schrieb:
> ISR (TIMER0_OVF_vect)
>
>   uint8_t sreg_temp;
>
>   sreg_temp = SREG;
>
>   sei();
>
>   ….. // z.B. SPI Interrupt (geringere Priorität)
>
>   SREG = sreg_temp;

Boah. Ist im besten Fall nicht nötig, weil ein Compiler für den AVR das 
Sichern des SREG selbst hinbekommen muß. Im schlechtesten Fall führt das 
in die "Katastrophe", weil C-Compiler durchaus Code optimieren (u.a. 
verschieben) dürfen und dann irgendein Quatsch passiert. Auf alle Fälle 
den Assembler-Output des Compilers kontrollieren, wenn man so was macht.
Aber in 99.99995% der Fälle gilt: Lass es den Compiler machen. Dafür ist 
er da.

Zu den sei(); im ISR:
Wenn der SPI Interrupt aber länger braucht, als der TIMER0_OVF_vect 
wieder überläuft, dann wird der TIMER0_OVF_vect nach dem RETI des 
SPI-Interrupt sofort nochmal aufgerufen, bevor der "1. TIMER0_OVF_vect" 
vollständig abgearbeitet ist. Hat das Potential u.a. zu Stack-Overflow 
und anderen sehr unerfreulichen und ekeligen Fehlern.

Tip von mir: Halte alle Interrupts so kurz, daß die kleine Verzögerung 
durch den Interrupt bei anderen verzögerten Interrupts nicht stört. 
Üblich ist z.B. nur einen Zähler hochzählen oder ein Flag zu setzten, 
welches dann  in der main() abgearbeitet wird. Dann kann man sich das 
sei(); im Interrupt sparen.
Es lohnt sich aus hier immer mal wieder einen kritischen Blick auf den 
Assembler-Output zu haben. Man kann durch geschicktes Programmieren hier 
einiges erreichen.

Faustformel:
Im Interrupt nur so viel wie absolut nötig, so kurz wie irgendwie 
möglich.


Gruß

Robert

P.S. Lesenswert zu "kurzen code" ist:

http://ww1.microchip.com/downloads/en/AppNotes/doc1497.pdf
http://ww1.microchip.com/downloads/en/AppNotes/doc8453.pdf

von Andi_T T. (andi_t)


Angehängte Dateien:

Lesenswert?

Jörg W. schrieb:
> Andi_T T. schrieb:
>> gcc version 5.4.0
>
> Der ist übrigens uuuuuuralt inzwischen (3. Juni 2016).


Ich habe das Atmel Studio 7 2020 installiert, wundert mich dann das 
diese alte Version hinterlegt ist, d.h. ich sollte ein Upgrade 
durchführen.
Wie wird dies am Besten durchgeführt damit kein Bruch entsteht, unter 
Extensions and Updates oder einfach die aktuelle Version unter 
https://gnutoolchains.com/avr/ downloaden und installieren!?

von Oliver S. (oliverso)


Lesenswert?

Andi_T T. schrieb:
> oder einfach die aktuelle Version unter
> https://gnutoolchains.com/avr/ downloaden und installieren!?

Da da die Version 5.3.0 angeboten wird, ist das wohl weniger sinnvoll.

Den (fast) aktuellsten avr-gcc findest du immer hier:

https://blog.zakkemble.net/avr-gcc-builds/

Aber wenn du mit dem Studio bisher klargekommen bist, und du da die 
aktuellste Version hast, gibts wenige Gründe für ein Update.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andi_T T. schrieb:
> Ich habe das Atmel Studio 7 2020 installiert, wundert mich dann das
> diese alte Version hinterlegt ist

Das kommt halt davon, wenn man sich als Hersteller seine eigenen Patches 
zusammen hackt (nennt sich bei Microchip "Device Pack"). Irgendwann 
läuft das dann mal mit den Upstream-Änderungen so weit aus dem Ruder, 
dass man es praktisch nicht mehr schafft, da alle eigenen Änderungen auf 
dem Laufenden zu halten.

Für dich als Endanwender hast du nun zwar eine Hersteller-gepflegte 
Software, die allerdings an einigen Ecken und Enden der 
Community-gepflegten (eben bspw. beim Prolog/Epilog der ISRs) deutlich 
hinterher hinkt.

: Bearbeitet durch Moderator
von Andi_T T. (andi_t)


Lesenswert?

Jörg W. schrieb:
> Das kommt halt davon, wenn man sich als Hersteller seine eigenen Patches
> zusammen hackt (nennt sich bei Microchip "Device Pack"). Irgendwann
> läuft das dann mal mit den Upstream-Änderungen so weit aus dem Ruder,
> dass man es praktisch nicht mehr schafft, da alle eigenen Änderungen auf
> dem Laufenden zu halten.

Hmm ok und was kann ich nun dagegen tun oder was wäre sinnvoll?

von Oliver S. (oliverso)


Lesenswert?


von Andi_T T. (andi_t)


Lesenswert?

Oliver S. schrieb:
> googlen ?

Ok, wollte nur nix falsch machen.

Vielen Dank an ALLE hier für den sehr guten Support.
Andreas

von Oliver S. (oliverso)


Lesenswert?

Andi_T T. schrieb:
> Ok, wollte nur nix falsch machen.

Warum? Wird man bei euch dann öffentlich geteert und gefedert?

Man lernt aus solchen Aktionen, aber nur, wenns erst mal schiefgeht, und 
man dann herausfindet, warum.

Oliver

von Andi_T T. (andi_t)


Lesenswert?

OK, da hast du allerdings recht :).

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.