Hallo Zusammen,
Ich hatte mir eben die avr/interrupt.h Datei angeschaut, um zu
verstehen, wie der sei() Befehl definiert ist.
Eigentlich dachte ich mir da steht sowas wie:
1
voidsei()
2
{
3
SREG=SREG|(1<<SREG_I);
4
}
Stattdessen steht aber folgendes:
1
#if defined(__DOXYGEN__)
2
/** \def sei()
3
\ingroup avr_interrupts
4
5
Enables interrupts by setting the global interrupt mask. This function
6
actually compiles into a single line of assembly, so there is no function
7
call overhead. However, the macro also implies a <i>memory barrier</i>
8
which can cause additional loss of optimization.
9
10
In order to implement atomic access to multi-byte objects,
11
consider using the macros from <util/atomic.h>, rather than
Kann mir einer bitte bestätigen, dass meine Idee auch richtig wäre? Mit
dem Interrupt-Flag im SREG werden ja die Interrupts frei gegeben? Der
einzige Unterschied wäre, dass meine Idee vielleicht ein bisschen
ineffizienter in der Ausführung wäre, oder nicht?
2. Wenn nein, wie kann ich diesen Begriff verstehen?
Alexander M. schrieb:> Zudem die Frage, was das mit dem DOXYGEN auf sich hat... Wann / wo wird> denn das definiert und was soll das bringen?
Doxygen ist ein Werkyeug zum Erstellen von Dokumentation, das Quellcode
"lesen" kann.
Alexander M. schrieb:> DOXYGEN
Das findest du raus....
Kurzform: DOXYGEN generiert Dokumentationen aus dem Quellcode.
Alexander M. schrieb:> 2. Wenn nein, wie kann ich diesen Begriff verstehen?# define sei()> _asm__ __volatile_ ("sei" ::: "memory")
Steht doch da im Kommentar haarklein beschrieben.
Alexander M. schrieb:> Aber für die Quellcode-Erzeugung müsste ja Doxygen dann aus sein, oder?
Nö. Doxygen ist ein extra Programm, das den Quelltext scannt und daraus
automatisch Dokumention erstellt. Es hat mit dem Compiler nix zu tun,
wenn gleich natürlich Doxygen die C-Syntax kennt. Alle Doxygenkommentare
sind normale C/C++ Kommentare, die der Compiler ignoriert.
Ja _DOXYGEN_ ist beim einfachen Compilieren nicht definiert. Das
scheint mit hier auch eher ein Workaround zu sein, da DOXYGEN wohl nicht
mit allen AVR spezifischen Besonderheiten klar kommt. Normalerweise ist
das viele #if define(...) ... #else ... #endif nicht nötig für DOXYGEN.
Deine Anweisung sollte theoretisch auch funktionieren, benötigt aber
mehrere Assembler Befehle. Ich bin kein AVR Assembler Experte aber wenn
ich das richtig überblicke wären mindestens 3 Befehle dafür notwendig.
Grob etwa:
* lade SREG in ein Arbeitsregister
* setze bit im Arbeitsregister
* schreibe Arbeitsregister zurück nach SREG
Der AVR kennt aber einen speziellen Befehl dafür, eben sei.
Mit der _asm_ Anweisung kann man direkt im C Code beliebige Assembler
Befehle eingeben.
"static" weil der Code in einem Header steht. Ohne "static" könnte es
mehrere Definitionen geben, wenn mehrere Module sei() verwenden und die
Funktion nicht geinlinet wird. Das würde in Linkerfehler (multiple
definitions of sei) resultieren.
Außerdem will man auf jeden Fall vermeiden, dass es einen
Funktionsaufruf gibt. Das kostet nur Zeit und Platz und erhöht die
Registerlast im Caller beträchtlich. Caller, die Leaf-Funktionen sind
(günstig) verlieren diesen Status (ungünstig).
Dann heißt die Funktion "sei", und es gibt eine AVR-Instruktion namens
"sei". Man würde also erwarten, dass die Funktion eben diesen Befehl
einfügt, folglich eine inline Assembly Zeile genau wie sie in
avr/interrupt.h zu finden ist. Oder eine Builtin-Funktion die gleiches
erledigt wie __builtin_avr_sei() (ab v4.7):
http://gcc.gnu.org/onlinedocs/gcc/AVR-Built-in-Functions.html
Und SREG |= 1 << SREG_I ist ungünstig, weil der Compiler daraus
1
in Rd, SREG
2
ori Rd, 1 << 7
3
out SREG, Rd
machen muss. Das sind 3 Instruktionen, 3 Ticks und Erhöhung der
Registerlast um 1 d-Register. SEI hingegen bewirkt das gleiche mit 1
Instruktion in 1 Tick und ohne ein Register zu brauchen.
> Stattdessen steht aber folgendes:> [...] Enables interrupts by setting the global interrupt mask.> This function [...]
Hier ist die Doku falsch, da es sich bei sei() nicht um eine Funktion
handelt sondern um ein funktionsähnliches Makro.
Hier wäre eine richtige Funktion (als static inline wie oben) ganz klar
zu bevorzugen, vor allem wenn der Header als C++ verwendet wird: Falls
eine Klasse eine Methode sei() hat, würde der Präprozessor eine
Makro-Ersetzung machen -> Bummer.
Leider verwendet die avr-libc diese Unart auch an anderen Stellen wie
bei abs(), siehe etwa
http://savannah.nongnu.org/bugs/?57114
Peter D. schrieb:> CLI, SEI sind eigenständige CPU-Befehle.> Ein Löschen, Setzen auf andere Art kann Seiteneffekte haben.
Nein, CLI und SEI sind Assembler-Mnemonics
und in diesen Fall nur Aliase zu den Mnemonics "BSET s" und "BCLR s"
Hier die 16-Bit Opcodes der Befehle:
CLI: 1001 0100 1111 1000
SEI: 1001 0100 0111 1000
BCLR s: 1001 0100 1sss 1000
BSET s: 1001 0100 0sss 1000
Wobei s die Bitnummer im SREG ist. Und das Interrupt Enable Bit, ist das
7. Bit
ck schrieb:> Peter D. schrieb:>> CLI, SEI sind eigenständige CPU-Befehle.>> Ein Löschen, Setzen auf andere Art kann Seiteneffekte haben.>> Nein, CLI und SEI sind Assembler-Mnemonics> und in diesen Fall nur Aliase zu den Mnemonics "BSET s" und "BCLR s"
Feenstaub interessiert hier nicht.
Sondern es geht darum, dass SEI / CLI was anderes ist als IN + BitOP +
OUT, was bei Operationan auf SREG, wie von Alexander M. vorgeschlagen,
erzeugt wird.
Bei AVR ist nur relevant ob das I-Flag im SREG gesetzt ist oder nicht.
Über welche Operation dies passiert ist egal.
Schau dir an, wie die Makros ATOMIC_BLOCK "util/atomic.h" aufgebaut ist.
Da passiert genau das, da wird das SREC-Register gesichert, CLI
aufgerufen und später der gesicherter Wert wieder zurückgeschrieben.
Stefan ⛄ F. schrieb:> Ich weiß nicht wovon ihr phantasiert, aber mein avr-gcc 5.4.0 mach aus> sei() und cli() die entsprechenden Assembler-Befehle:
Es geht darum, dass SREG = SREG | (1 << SREG_I) kein SEI generiert.
Und nicht darum, ob sei() ein SEI erzeugt.
ck schrieb:> Bei AVR ist nur relevant ob das I-Flag im SREG gesetzt ist oder nicht.> Über welche Operation dies passiert ist egal.
Also Ausführungezeit, Speicherverbrauch und Registerbelegung spielen
schon ne Rolle in jedem realen Programm. Find ich zumindest.
> Schau dir an, wie die Makros ATOMIC_BLOCK "util/atomic.h" aufgebaut ist.>> Da passiert genau das, da wird das SREC-Register gesichert, CLI> aufgerufen und später der gesicherter Wert wieder zurückgeschrieben.
Genau. Es wird CLI verwendet um das I-Flag zu löschen und eben nicht
per Operation auf SREG.
Johann L. schrieb:> ck schrieb:>> Peter D. schrieb:>>> CLI, SEI sind eigenständige CPU-Befehle.>>> Ein Löschen, Setzen auf andere Art kann Seiteneffekte haben.>>>> Nein, CLI und SEI sind Assembler-Mnemonics>> und in diesen Fall nur Aliase zu den Mnemonics "BSET s" und "BCLR s">> Feenstaub interessiert hier nicht.
Interessiert und wie, dasselbe Problem hatte ich mit CLR reg.
Da kamen dann gleich die Korinthenkacker mit klugen Erklärungen wie:
dieser Befehl existiert gar nicht, das ist ein EOR reg mit sich
selbst, du hast keine Ahnung und ähnliches (vielleicht erkennt sich
ein Moderator hier).
Meine Versuche zu erklären, dass CLR übersichtlicher, kürzer und
praktisch selbsterklärend ist, scheiterten kläglich...
Also, BCLR 7 ist demzufolge weitaus übersichtlicher und zeugt von
überlegenem Assemblerwissen, wogegen CLI nur Unwissende benutzen...
Stefan ⛄ F. schrieb:> Ich weiß nicht wovon ihr phantasiert, aber mein avr-gcc 5.4.0 mach aus> sei() und cli() die entsprechenden Assembler-Befehle:
[...]
Auch dann, wenn _DOXYGEN_ definiert ist?
Darauf wollte der Fragesteller wohl eigentlich hinaus. Also auch ich
lese das erstmal so, dass in diesem Fall dies zur "Anwendung" kommt:
#define sei()
d.h. ein function-like Makro, was genau garnix tut, also insbesondere
keinen Code erzeugt, weder ein Asm-"sei" noch sonst irgendwas.
Nein, dieser Schrott ist alles andere als selbsterklärend. Hier wird der
Prepzessor vergewaltigt zur Generierung fast nutzloser Pseudo-Doku. Mit
dem Seiteneffekt, dass man den Code nicht mehr leicht lesen kann. Das
kann's ja wohl nicht sein...
c-hater schrieb:> #define sei()>> d.h. ein function-like Makro, was genau garnix tut, also insbesondere> keinen Code erzeugt,
Nö. Nennt sich "Synopsis".
Man sieht daran, dass es ein funkcion-like Makro ist, dass genau 0
Argumente bekommt.
Bei sei() könnte man die Definition noch hinzutun, aber mach das mal bei
ausgewachsenen, konditionalen Makros wie pgm_read_dword. Das will man
in der Doku nicht sehen. Was man da lesen will ist
* Wie wird es verwendet?
* Was macht es?
Bei einer echten Funktion würde man ja auch nur den Prototyp
hinschreiben und nicht die ganze Implementation ausbreiten.
Johann L. schrieb im Beitrag #6496233:
> ...und schon ist unser Herzchen wieder ganz bei sich.
Genau wie du: Wenn es darum geht, den ganzen C/C++-Wahnsinn auf die
Ebene logisch denkender Menschen herunterzubrechen, hast du erhebliche
Artikulationsprobleme...
Also, du C-Verliebter, erkläre doch bitte mal, woran man bei diesem
Konstrukt einfach erkennen kann, dass letztlich immer doch der
#else-Zweig "ausgeführt" wird. (Gemeint ist natürlich: zur Compiletime)
ICH weiss natürlich, dass das so ist und kann auch erklären, warum das
so ist.
Aber aus dem Quelltext kann man das definitiv nicht einfach entnehmen...
Sowas halte ich für eine extreme Sprach-Vergewaltigung. Ja, sie ist
möglich, und ist nach der Sprachdefinition legal. Aber sie ist KRANK,
KRANK, KRANK.
Allein die Möglichkeit dafür macht die Sprache KRANK, KRANK, KRANK.
c-hater schrieb:> Aber aus dem Quelltext kann man das definitiv nicht einfach entnehmen...
Wenn man fremden Code liest, muss man mir so etwas klar kommen. Aufregen
bringt da gar nichts. Wer meint es besser machen zu können: Nur zu. Wenn
es wirklich signifikant besser ist, wird es bald zum Standard.
Man kann den Compiler dazu bringen, den Quelltext des effektiv wirksamen
C Codes nach Auflösung aller Makros auszugeben. Der wäre dann leichter
lesbar. Und speziell für dich: Der Compiler kann auch den generierten
Assembler Code ausgeben.
Damit kann er schon zwei Dinge, die du bei fast keiner anderen
Programmiersprache finden wirst.
Stefan ⛄ F. schrieb:> Aufregen bringt da gar nichts.
c-hater liest Code, genau um sich aufzuregen.
Wenn er sich über Code nicht aufregt, hat der Code seinen Sinn verfehlt.
ck schrieb:> Peter D. schrieb:>> CLI, SEI sind eigenständige CPU-Befehle.>> Ein Löschen, Setzen auf andere Art kann Seiteneffekte haben.>> Nein, CLI und SEI sind Assembler-Mnemonics
Haarspalterei
> und in diesen Fall nur Aliase zu den Mnemonics "BSET s" und "BCLR s"
Die Frage ist doch eher, wer hier der Alias für wen ist. Ich bin (sicher
nicht als einziger) der Überzeugung, daß SEI/CLI die primären Mnemonics
sind. Jede CPU, die Interrupts unterstützt, hat einen Maschinenbefehl,
der Interrupts atomar ein- bzw. ausschaltet. Es ist nur folgerichtig,
daß dieser Befehl entsprechend benannt ist.
Daß andererseits ein solcher binärer (an oder aus) Zustand irgendwo in
einem einzelnen Bit gespeichert wird, ist ebenso trivial. Und wenn das
Bit in einem öffentlich zugänglichen Register steht, dann gibt es auch
eine Möglichkeit, einen alternativen Mnemonic zu kreieren.
Im Prinzip mag ich, was der AVR Assembler da macht: alternative
Mnemonics für die gleiche Instruktion zu erlauben. Wenn man das richtig
einsetzt, kann man die Lesbarkeit eines Programms signifikant erhöhen.
Andererseits kann man durch das sture Beharren darauf, daß nur eine der
ALternativen "richtig" wäre, das Verständnis eines Programms auch
erschweren.
Axel S. schrieb:> Andererseits kann man durch das sture Beharren darauf, daß nur eine der> ALternativen "richtig" wäre, das Verständnis eines Programms auch> erschweren.
Hab dich nicht so, so ne Turingmaschine ist doch quasi Hochsprache^3!!!
;-)
Axel S. schrieb:> Die Frage ist doch eher, wer hier der Alias für wen ist. Ich bin (sicher> nicht als einziger) der Überzeugung, daß SEI/CLI die primären Mnemonics> sind. Jede CPU, die Interrupts unterstützt, hat einen Maschinenbefehl,> der Interrupts atomar ein- bzw. ausschaltet. Es ist nur folgerichtig,> daß dieser Befehl entsprechend benannt ist.
Nochmal, für die AVR-CPU ist es nur relevant, ob das I-Bit im SREG
gesetzt ist oder nicht.
Und ein Atomarer Befehl für das Setzen und Löschen ist BSET 7 und BCLR
7. SEI/CLI sind dann Aliase dafür, die auch zur besseren Lesbarkeit des
Programms beitragen.
Wenn du das AVR Instruction Set Handbuch durch gehst, wirst du
feststellen das es viele solcher Aliase gibt. Ein "CLR Rd" ist nur eine
spezialform von "EOR Rd,Rr" wo Rd und Rr identisch sind. Es kommt in
beiden Fällen der gleiche Opcode raus.
Und ein read-modify-write auf SREG um das I-Flag zu ändern funktioniert
ebenfalls. Es hat den gleichen Effekt. Ist aber natürlich nicht Atomar
und brauch mehr Programcode.
ck schrieb:> Nochmal, für die AVR-CPU ist es nur relevant, ob das I-Bit im SREG> gesetzt ist oder nicht.
Für mich ist hingegen relevant, ob Interrupts erlaubt sind, oder
nicht. Die Programmiersprache ist mein Diener, nicht umgekehrt.
ck schrieb:> Axel S. schrieb:>> Die Frage ist doch eher, wer hier der Alias für wen ist. Ich bin (sicher>> nicht als einziger) der Überzeugung, daß SEI/CLI die primären Mnemonics>> sind. Jede CPU, die Interrupts unterstützt, hat einen Maschinenbefehl,>> der Interrupts atomar ein- bzw. ausschaltet. Es ist nur folgerichtig,>> daß dieser Befehl entsprechend benannt ist.>> Nochmal, für die AVR-CPU ist es nur relevant, ob das I-Bit im SREG> gesetzt ist oder nicht.
Nochmal: das ist bekannt und unbestritten und trivial.
> Und ein Atomarer Befehl für das Setzen und Löschen ist BSET 7 und BCLR> 7. SEI/CLI sind dann Aliase dafür, die auch zur besseren Lesbarkeit des> Programms beitragen.
Nochmal: Mnemonics sind im Prinzip beliebig. Allerdings heißen sie nicht
umsonst so. Der Name kommt vom griechischen mnēmoniká = Gedächtnis.
Der wesentliche Punkt bei der Wahl eines Mnemonics ist, daß er gut
merkbar sein sollte und die Wirkung des Maschinenbefehls gut beschreibt.
Deswegen ist SEI besser [1] als BSET 7
CLR reg ist besser als EOR reg,reg
ADDI reg,10 ist besser als SUBI reg,-10
> Wenn du das AVR Instruction Set Handbuch durch gehst, wirst du> feststellen das es viele solcher Aliase gibt.
Und nochmal: das ist bekannt. Uns allen. Du bist nicht weise, nur
Durchschnitt.
[1] noch besser wäre EI statt SEI für enable interrupts. Und dann
natürlich auch DI statt CLI. Bin ich alt?
Der 8051 hat dafür keinen Befehl. Da muß man schreiben:
1
EA=1;// bzw.
2
EA=0;
Das EA-Bit wird beim Interrupteintritt nicht gelöscht, bzw. beim RETI
nicht gesetzt. Die Interruptlogik merkt sich intern den gerade aktiven
Interruptlevel.
Axel S. schrieb:> [1] noch besser wäre EI statt SEI für enable interrupts. Und dann> natürlich auch DI statt CLI.
EI und DI wären auch meiner Meinung nach besser, weil eindeutiger.
Ich glaube, die Atmel Leute wollten konsequent sein mit CLx und SEx.
Also CL+I für Interrupt, CL+T für T-Flag, CL+R für Register usw.
Und SE+I für Interrupt, SE+T für T-Flag, SE+R für Register usw.
Mich persönlich haben immer die BLD und BST genervt, muss immer noch
überlegen was wohin geladen wird.
> Bin ich alt?
Hat in diesem Fall nichts mit Alter zu tun, eher mit Logik.
Axel S. schrieb:>> Nein, CLI und SEI sind Assembler-Mnemonics>> Haarspalterei>>> und in diesen Fall nur Aliase zu den Mnemonics "BSET s" und "BCLR s">> Die Frage ist doch eher, wer hier der Alias für wen ist. Ich bin (sicher> nicht als einziger) der Überzeugung, daß SEI/CLI die primären Mnemonics> sind. Jede CPU, die Interrupts unterstützt, hat einen Maschinenbefehl,> der Interrupts atomar ein- bzw. ausschaltet. Es ist nur folgerichtig,> daß dieser Befehl entsprechend benannt ist.
Ach nö, das alles ist nur mal wieder eine Frage des "Stallgeruches",
also dem näheren Umfeld geschuldet. EI und DI sind in den klassischen
Gefilden von Intel eher Mode und SEI und CLI haben sich eben die
Atmel-Leute ausgedacht. Leute aus dem jeweils anderen Basislager
empfinden das gegenseitig als unleserlich.
Und deine Ansicht, daß jede CPU, die Interrupts unterstützt, auch
einen generellen Befehl zum ein- und ausschalten hat, stimmt so auch
nicht. Beim klassischen ARM ist sowas zum Beispiel an den Betriebsmodus
geknüpft, also ob die CPU grad Code im User-Modus abarbeitet oder im
Supervisor-Modus ist oder im Interruptmodus oder im Fast-Interrupt-Modus
und so weiter. Dazu gibt es dann auch unterschiedliche Stacks (und
z.T. unterschiedliche Registersätze). Nur so als Beispiel.
Ich halte es ohnehin für ausgesprochen schlechten Stil, mit sowas wie
EI/DI bzw. SEI/CLI im Programm herumzutoben. Wenn Interrupts, dann
sollten die eben immer eingeschaltet sein. IMMER! Das geht. Es sind
lediglich nur Anfänger, die damit nicht klarkommen, weil sie noch zu
wenig Programmierkenntnisse haben. Das Argument "ja wenn ich doch grad
genau DIES machen will, dann MUSS ich.." gilt nicht. Man macht eben
nicht "DIES", sondern man macht es anders.
Ich stell mir grad mal vor, wie so einer bei einer CPU programmieren
wollte, die im User-Mode sowas wie DI/EI schlichtweg nicht ausführt oder
gar eine Exception wirft, wenn man das probiert.
W.S.
W.S. schrieb:> Ich halte es ohnehin für ausgesprochen schlechten Stil, mit sowas wie> EI/DI bzw. SEI/CLI im Programm herumzutoben
Das sei dir unbenommen, aber wenn es der Prozessor erfordert, dann ist
das so. Ganz ohne geht es nicht.
Oliver
Marc V. schrieb:> Axel S. schrieb:
...
>> Bin ich alt?> Hat in diesem Fall nichts mit Alter zu tun, eher mit Logik.
Ich habe die Mnemonics EI und DI sozusagen "mit der Muttermilch
eingesaugt", weil die erste Architektur, zu der ich ein intimes
Verhältnis hatte, die verwendet hat. Es war der Z80.
(genauer gesagt: der U880)
W.S. schrieb:> Ach nö, das alles ist nur mal wieder eine Frage des "Stallgeruches",> also dem näheren Umfeld geschuldet. EI und DI sind in den klassischen> Gefilden von Intel eher Mode und SEI und CLI haben sich eben die> Atmel-Leute ausgedacht.
Du hast meinen Punkt nicht verstanden. Ein Mnemonic soll möglichst klar
und eindeutig beschreiben, welche Wirkung ein Maschinenbefehl hat.
SEI/CLI respektive EI/DI beeinflussen die Interruptlogik dahingehend,
daß die Interrupts akzeptiert oder nicht. Die Worte enable bzw.
disable sind da eindeutig und unmißverständlich. Bei set (bit) und
clear (bit) weiß ich zwar sofort, ob das Bit gesetzt ist oder nicht.
Aber ich brauche immer noch zusätzliches Wissen darüber, ob das gesetzte
Bit Interrupts nun erlaubt, oder sperrt. Nur durch Lesen des Codes
erfahre ich das nicht.
Und deswegen ist EI der bessere Mnemonic gegenüber SEI.
> Ich halte es ohnehin für ausgesprochen schlechten Stil, mit sowas wie> EI/DI bzw. SEI/CLI im Programm herumzutoben.
Das ist Unsinn. Bevor man Interrupts enabled, muß man praktisch immer
irgendwas initialisieren oder konfigurieren. Und da AVR (um diese
Architektur geht es ja in diesem Thread) keine atomaren Zugriffe auf
Werte länger als 8 Bit erlaubt, braucht man hin und wieder auch mal eine
CLI/SEI Klammer um einen solchen Zugriff. Natürlich zweckmäßigerweise in
ein Makro gekapselt.
Axel S. schrieb:>> Ich halte es ohnehin für ausgesprochen schlechten Stil, mit sowas wie>> EI/DI bzw. SEI/CLI im Programm herumzutoben.>> Das ist Unsinn.
Dein Gespräch mit W.S. ist es nicht weniger . . .
Marc V. schrieb:> Mich persönlich haben immer die BLD und BST genervt, muss immer noch> überlegen was wohin geladen wird.
Load: Vom Speicher ins CPU-Register laden
Store: Wert vom CPU-Register in den Speicher schreiben.
SREG ist zwar kein normaler Speicher, kann aber so zugegriffen werden,
insbesondere auch via LDS und STS wenn man die RAM-Adresse von SREG
verwendet. LDS und STS lädt bzw. speichert alle Bits, BLD und BST eben
nur das T-Bit von SREG.
Also ganz einfach zu merken :-)
Johann L. schrieb:> Load: Vom Speicher ins CPU-Register laden
[Eselsbrücke]
Wenn ich einen "geladen" habe, ist der Fusel im Kopp (CPU) und nicht
mehr in der Flasche (Speicher).
[/Eselsbrücke]
;-)
Falk B. schrieb:> Wenn ich einen "geladen" habe, ist der Fusel im Kopp (CPU) und nicht> mehr in der Flasche (Speicher).
Und wenn ich einen geladen habe, ist mir das alles so was von egal...