Hallo Tom,
soweit ich das verstanden habe, dienen die Events dem 'vermeiden' der
ISR. Der Controller soll durch einen Event aus dem Schalf gerissen
werden, ohne aber die anschließende Behandlung durch innerhalb der ISR
auszlösen. Die Programmausführung soll dynamisch auf so ein 'Event'
reagieren können, je nach dem, ob es gerade zeitlich passt oder nicht.
Das setzt ein Event-'Betriebssystem' vorraus.
MfG
Events sind neben Interrupts etwas, das dem Cortex Core seinen Schlaf
rauben kann. Wenn der also in irgendeinem Sleep-Mode pennt und dafür WFE
verwendet hat, dann können ihn auch Events aufwecken.
Siehe Kapitel 2.5 in PM0056.
Wobei man bei der Lektüre etwas kreativ denken muss. So steht dort "The
processor provides an external event input signal" ohne zu erwähnen,
dass dieses "external" auch intern generiert sein kann. Wohl aus der
Perspektive des Cores zu sehen, für den alles ausserhalb seiner selbst
"extern" ist.
Mit solchen Events lässt sich beispielsweise das Problem
verlorengehender Ereignisse vermeiden. Bei sleep modes tritt nicht
selten ein Problem auf, wenn der Controller drauf und dran ist sich
schlafen zu legen und exakt davor das Ereignis auftritt, das ihn wieder
aufwecken soll. Diese race condition sollte man bei sleep modes egal
welcher Controller immer im Auge behalten.
Bei AVRs löst man das Problem, indem man erst unmittelbar vor dem
SLEEP-Befehl die Interrupts freigibt und dies im Prozessor einen Befehl
verzögert erst mit dem SLEEP-Befehl wirksam wird. Andere Prozessoren
besitzen deshalb SLEEP-Befehle mit eigener Interrupt-Steuerung.
Beim Cortex sieht das etwas anders aus. Man kann m.W. die Priorität
nicht exakt gleichzeitig zum WFI-Befehl setzen, so dass man in ein
Dilemma gerät. Wenn man die Priorität so setzt, dass der Handler nicht
vor WFI durchkommt, dann wacht er auch im WFI nicht auf. Wenn man sie so
setzt, dass der Interrupt durchkommt, dann läuft die ISR u.U.
unmittelbar vor dem WFI-Befehl und der Controller legt sich danach über
den WFI-Befehl schlafen. Wenn dieser Interrupt ihn jedoch dauerhaft
aufwecken sollte, dann verpennt der Controller.
Mit dem Event-Bit löst sich das Problem, indem der betreffende Interrupt
dieses Bit setzt und man für den sleep mode WFE verwendet. Dieser Befehl
ist bei gesetztem Bit wirkungslos und der Controller pennt nicht ein.
Die Verwendung der eigentlich für multicores vorgesehenen events des
Cortex-M3 cores für wakeup aus sleep modes scheint eine Besonderheit der
STM32 zu sein. Jedenfalls bin ich etwas Vergleichbaren bei den LPC1000
nicht begegnet.
Es würde mich folglich interessieren wie das Problem der oben
skizzierten race condition bei den LPC1000 gelöst wird.
A. K. schrieb:> Bei sleep modes tritt nicht> selten ein Problem auf, wenn der Controller drauf und dran ist sich> schlafen zu legen und exakt davor das Ereignis auftritt, das ihn wieder> aufwecken soll. Diese race condition sollte man bei sleep modes egal> welcher Controller immer im Auge behalten.
Dieses Problem wurde aber erst durch eine völlig unsinnige Empfehlung in
den Atmel Datenblättern provoziert.
Das Sleep Enablen direkt vor dem Sleep ist großer Quatsch und führt fast
unweigerlich zum Deadlock.
Wenn man im Main den Sleepmode zuerst, d.h. vor dem Interrupt enabled
und der Interrupt den Sleepmode disabled, ist alles in Butter. Egal, wer
zuerst kommt.
Peter
Peter Dannegger schrieb:> Wenn man im Main den Sleepmode zuerst, d.h. vor dem Interrupt enabled> und der Interrupt den Sleepmode disabled, ist alles in Butter. Egal, wer> zuerst kommt.
Das löst das zugrunde liegende Problem nur teilweise. Denn auch bei
abgeschaltetem sleep mode läuft der Controller nach dem einen Hauch zu
früh eintreffenden Interrupt sofort in den SLEEP Befehl rein. Zwar dann
ohne Tiefschlaf, aber dennoch reagiert die Hauptschleife nun nicht auf
die Tätigkeit, die von der ISR möglicherweise dort angestossen wurde.
Erst wenn der nächste Interrupt kommt geht es wie vorgesehen weiter.
Dieses Problem ist grundsätzlicher Natur, nicht auf AVRs beschränkt und
nicht auf Powersave-Modi. Sobald zwischen der Freigabe des Interrupts
und dem folgenden SLEEP/WAIT/HALT/... eine Lücke auftritt durch die ein
Interrupt schlüpfen kann hat man dieses Problem. PIC18 schaltet im
entsprechenden Befehl die Interrupts selbst ein, andere reagieren wie
AVR und ARM7/9(*) bei der Freigabe der Interrupts erst mit dem
Folgebefehl, 68K läd darin die Interrupt-Priorität, ...
Die Event-Variante der Cortex-M3 weicht jedoch von diesem klassischen
Prinzip ab. Es ist die einzige mir bekannte Variante, in der es ohne
Abschaltung der Interrupts (oder Code im RAM) sauber lösbar ist, und das
ist wohl auch der Sinn der Sache. Der Latenzzeit zuliebe.
*: Dessen "surprise interrupt" bei abschaltenden Interrupts ist die
direkte Folge dieser Strategie.
A. K. schrieb:> Das löst das zugrunde liegende Problem nur teilweise. Denn auch bei> abgeschaltetem sleep mode läuft der Controller nach dem einen Hauch zu> früh eintreffenden Interrupt sofort in den SLEEP Befehl rein. Zwar dann> ohne Tiefschlaf, aber dennoch reagiert die Hauptschleife nun nicht auf> die Tätigkeit, die von der ISR möglicherweise dort angestossen wurde.> Erst wenn der nächste Interrupt kommt geht es wie vorgesehen weiter.
Dann ist dort der Sleep-Befehl grundsätzlich anders als beim AVR.
Wird beim AVR der Sleepmode disabled, arbeitet der Sleep-Befehl nur wie
ein NOP, d.h. da wartet nichts.
Will man aber auf einen Interrupt warten, wird der Sleepmode direkt nach
dem letzten Sleep enabled. Kommt dann bis zum nächsten Sleep kein
Interrupt, wartet das Sleep. Dadurch geht keine Nachbehandlung eines
Interrupt im Main verloren.
> Die Event-Variante der Cortex-M3 weicht jedoch von diesem klassischen> Prinzip ab.
Dann muß aber nach dem Sleep das Interruptflag noch händisch gelöscht
werden.
Peter
Peter Dannegger schrieb:> Wird beim AVR der Sleepmode disabled, arbeitet der Sleep-Befehl nur wie> ein NOP, d.h. da wartet nichts.
Yep, dann löst sich das Problem.
Das scheint mir aber die Ausnahme zu sein. Bei den meisten Architekturen
wartet der entsprechende Befehl immer auf eintreffende Interrupts,
konfigurierbar ist nur die Frage, wie tief er dabei schläft.
> Dann muß aber nach dem Sleep das Interruptflag noch händisch gelöscht> werden.
Wenn WFE durch ein Event aufgeweckt wird, dann wird das Event-Bit
dadurch automatisch gelöscht. Das ist auch gut so, denn sonst würde man
auch wieder in einer race condition landen.
Interrupt-Flags müssen nicht gelöscht werden, weil nicht unbedingt
welche gesetzt werden, wenn das Ereignis nur aufwecken soll. Jedenfalls
nicht auf dem STM32, denn das Event-Signal an den Core ist transient
(Puls), zum Event-Bit wird es erst im Core.
Bei anderen Controllern auf Cortex-Basis sieht das etwas anders aus, da
die zwar das Event-Bit ebenfalls dafür verwenden, aber das Event nicht
wie STM32 im EXTI separat auslösen können, sondern nur über Interrupts.
Kann sein, dass man in irgendwelchen Devices nachbearbeiten muss.
Vielleicht will man ja auch in die dazu gehörende ISR reinlaufen, aber
erst nachdem im Rahmen des Aufweckvorgangs wieder der richtige Takt
usw. aktiviert wurde.
An diesem Beispiel sieht man, daß es in verschiedenen MCs auch
verschiedene Wege gibt, eine bestimmte Aufgabe zu lösen.
Daher ist es doch nicht so leicht, schnell mal eben zwischen
verschiedenen MCs zu wechseln.
Und es ist besser, wenige MCs richtig zu beherrschen, als viele nur
halb.
Denn nur dann kann man auch zuverlässige Programme schreiben.
Man muß sich schon eingelesen haben, wie der MC intern arbeitet, damit
man den Programmablaufplan fehlerfrei erstellen kann.
Ich muß auch sagen, Hut ab, wer die Interruptlogik der ST-ARM7
vollständig verstanden hat (ich habe es nämlich nicht).
Peter
Peter Dannegger schrieb:> Ich muß auch sagen, Hut ab, wer die Interruptlogik der ST-ARM7> vollständig verstanden hat (ich habe es nämlich nicht).
Die Timer vom STM32 sind auch nicht übel ;-). Ich kann deshalb
Controller dieser Klasse auch nicht für den Einstieg empfehlen,
mmvisuals missionarischem Eifer zum Trotz.
Peter Dannegger schrieb:> Ich muß auch sagen, Hut ab, wer die Interruptlogik der ST-ARM7> vollständig verstanden hat (ich habe es nämlich nicht).
ST ARM7 wäre ja wohl STR7, also den EIC. Hab grad kurz reingesehen -
verglichen mit dem Krampf den NXP über die ARM PrimeCells in die LPC2000
eingebaut hat (und ST in den STR9, und das auch noch doppelt) ist das
Teil doch prima, und vor allem besser dokumentiert.
A. K. schrieb:> ST ARM7 wäre ja wohl STR7, also den EIC.
Ja, ist schon einige Jährchen her, daß ich ihn mir angeguckt habe.
> und vor allem besser dokumentiert.
Ja, 35 Seiten über die Interruptlogik ist schon sehr beeindruckend.
Und dann die vielen Note, Avoid, Caution, Warning.
Z.B.:
"Note
The Interrupt Pending bits must be carefully handled because the EIC
state machine and its internal priority hardware stack could be forced
to a non recoverable condition if unexpected pending bit clear
operations are performed."
Peter
Peter Dannegger schrieb:> Ja, 35 Seiten über die Interruptlogik ist schon sehr beeindruckend.
Yep, aber ich habe lieber ein paar Seiten mehr als die fehlenden Seiten
bei den ARM PrimeCells. Dort ist nämlich der für's Nesting wichtige Teil
dermassen konsequent unter den Tisch gefallen, dass manche sie als dafür
effektiv ungeeignet abqualifizieren und wieder andere sich fragen wie
das überhaupt funktionieren kann. Und einfach mal in der Doku
zwischendrin darauf hinzuweisen, dass der VIC eigentlich sowieso
ungeeignet für alles ist (so jedenfalls lesen manche den entsprechenden
Passus) ist auch nicht wirklich lustig.
Aber ARM hat aus dem Graus der vielen komplexen Interrupt-Controller der
ARM7/9 den richtigen Schluss gezogen und mit dem NVIC der Cortexe klar
Schiff gemacht. Ist dadurch zwar nicht so arg viel einfacher geworden
(geht bei dem Thema nicht), aber einheitlich. Wirklich übersichtlich
sind eigentlich nur die ARM7er von Analog Devices - die haben schlicht
keinen Interrupt Controller eingebaut.
Peter Dannegger schrieb:> The Interrupt Pending bits must be carefully handled because the EIC> state machine and its internal priority hardware stack could be forced> to a non recoverable condition if unexpected pending bit clear> operations are performed."
Das liest sich auch nicht schlimmer als manche Bugs quer durch die
Controllerwelt. NXP hat in den LPC2100/2200 einen CAN Controller drin,
der dank eines Bug die halbe Funktionalität verloren hat. Der CAN
Controller diverser PICs kann sich durch fast normale Busverhältnisse
und Nichtstun in Nullkommanix in eine ähnliche "unrecoverable condition"
manövrieren und ziemlich viele CAN Controller von Microchip reichen bei
bestimmten undramatischen Busproblemen defekte Pakete als korrekt durch.
So geht das endlos weiter, egal wohin man schaut.
Peter Dannegger schrieb:
> Und es ist besser, wenige MCs richtig zu beherrschen, als viele nur> halb.> Denn nur dann kann man auch zuverlässige Programme schreiben.> Man muß sich schon eingelesen haben, wie der MC intern arbeitet, damit> man den Programmablaufplan fehlerfrei erstellen kann.
Dem kann ich nur zustimmen.
Und weil es gerade so schön zu dem Thema passt -
eine schöne Überraschung hält der sleep-Befehl beim AVR
auch noch bereit, wenn man ihn mit Timer-Interrupt und
ADC-Interrupt kombiniert. Wer liest schon den unauffällig
platzierten Hinweis im Datenblatt zu sleep, dass der
Befehl nebenher noch eine AD-Wandlung lostritt...
Zum Glück arbeite ich vorwiegend in Assembler - ich kann
mir vorstellen, dass diese Falle C-Anwendern noch viel
eher verschlossen bleibt. Muss aber gestehen, dass ich
auch unfreiwillig darüber gestolpert bin. :-)
Wo so etwas passieren kann?
Wenn man mit Timer-Interrupt AD-Daten zu fixen Intervallen
erfassen möchte und auch die AD-Wandlung per Interrupt
erfolgt und zwecks Energeieinsparung (Datenlogger) in der
untätigen Zeit sleep benutzt wird.
guest schrieb:> Zum Glück arbeite ich vorwiegend in Assembler - ich kann> mir vorstellen, dass diese Falle C-Anwendern noch viel> eher verschlossen bleibt.
Was hat das denn mit C vs. Assembler zu tun? Datasheet lesen muss man so
oder so. Oder hältst du Assembler-Programmierer für die Elite der Zunft?
Ich finde ja die Interruptlogik des 8051 genial einfach:
Jede Priorität hat ein Bit, also 4 Bits bei 4 Prioritäten.
Jeder pending Interrupt prüft, ob Bits gleich oder größer seiner
Priorität gesetzt sind. Wenn nicht, dann setzt er das seiner
Eintrittspriorität entsprechende Bit und springt seinen Vektor an.
Jedes RETI löscht dann anfangend vom höchsten wieder ein Bit.
Damit sind verschiedene "Schweinereien" möglich aber "non recoverable
conditionen" unmöglich.
Z.B. kann man eine Priorität verlassen, indem innerhalb des
Interrupthandlers ein RETI ausgeführt wird.
Oder man kann auch innerhalb des Interrupts die Priorität ändern, sie
wird sofort nach dem nächsten Befehl gültig. Ein Interrupt kann sich
damit bis zu 3-mal selbst unterbrechen.
Das RETI benötigt die Kenntnis der Eintrittspriorität nicht mehr, da es
ja immer nur das höchstwertigste Prioritätsbit löscht.
Die ganze Interruptprioritätslogik des 8051 besteht also nur aus 4 Bits.
Wow.
Peter
Peter Dannegger schrieb:> Die ganze Interruptprioritätslogik des 8051 besteht also nur aus 4 Bits.
Ich hatte oben schon die ADUC7000 angeführt. Da sind es sogar nur 2. Und
beim AVR nur ein einziges. Wow!
A. K. schrieb:> beim AVR nur ein einziges. Wow!
Kein "Wow!", denn das ist wirklich schade bei den Mega/Tiny-AVRs, daß
die keine Prioritäten haben.
Peter
Peter Dannegger schrieb:> Kein "Wow!", denn das ist wirklich schade bei den Mega/Tiny-AVRs, daß> die keine Prioritäten haben.
Peter, ich habe dich nur mit deinen 4 Bits auf den Arm genommen.
Natürlich hast du recht.
> Was hat das denn mit C vs. Assembler zu tun? Datasheet lesen muss man so> oder so. Oder hältst du Assembler-Programmierer für die Elite der Zunft?
funktioniert doch immer wieder :-)
locker bleiben!
Peter Dannegger schrieb:> Ja, 35 Seiten über die Interruptlogik ist schon sehr beeindruckend.> Und dann die vielen Note, Avoid, Caution, Warning.
Das fiese an dem Teil (EIC von STR7) ist so weit ich sehe, dass man am
Ende des Handlers das toplevel pending bit selber rausfinden und
explizit zurücksetzen muss. Und dass man da, wenn man das falsche Bit
erwischt, Unfug treiben kann. Das ist ausgesprochen doof, wenn der
gleiche Handler für mehrere Quellen zuständig ist - insbesondere wenn
man auf ARM7 einen zentralen Wrapper für alle Interrupts verwendet, der
den Verwaltungskram macht.
Üblich ist eigentlich, dass der Interrupt-Controller ein eher allgemein
gehaltenes "End Of Interrupt" mitgeteilt bekommt und er aufgrund seiner
Zustandsinformationen selber spitz kriegt, wer gemeint ist.
Peter Dannegger schrieb:> Jeder pending Interrupt prüft, ob Bits gleich oder größer seiner> Priorität gesetzt sind. Wenn nicht, dann setzt er das seiner> Eintrittspriorität entsprechende Bit und springt seinen Vektor an.> Jedes RETI löscht dann anfangend vom höchsten wieder ein Bit.
Diese Beschreibung könnte auch ganz gut die Technik beiden VIC Varianten
der ARM PrimeCells erklären (z.B. LPC2000). Die präzise Beschreibung der
Prioritätsmechanik fehlt dort völlig, und das sorgt für Irritation.
Nachteil könnte sein, dass man m.E. weder beim 8051 noch beim VIC
rauskriegt, auf welchem priority level man grad läuft, weil diese
Information nur als bitmap in einem nicht zugänglichen pending interrupt
register steht. Generell taucht in beiden Versionen die interrupt
priority nur recht sparsam auf. Ein explizites Sperren aller interrupts
unter einem bestimmten priority level existiert nicht.
In einfacher gestrickten Controller-Programmen spielt das kaum eine
Rolle. OS-Kernels, ob grosse oder kleine (RTOS), arbeiten jedoch gerne
mit solchen priority levels, gehen gerne auch mal ad hoc rauf und
runter, und grad der NVIC der Cortexe kommt dieser Arbeitsweise sehr
entgegen.
A. K. schrieb:> Diese Beschreibung könnte auch ganz gut die Technik beiden VIC Varianten> der ARM PrimeCells erklären (z.B. LPC2000). Die präzise Beschreibung der> Prioritätsmechanik fehlt dort völlig, und das sorgt für Irritation.
Ich kann jetzt auch nicht mehr sagen, wo ich das mit den Bits der
Interruptlogik des 8051 gelesen hab. Es steht jedenfalls nicht in den
üblichen Datasheets/Manuals.
Es ist aber doch sehr wichtig, um die Funktion zu verstehen.
Maxim (Dallas) hat auch einige 8051, wo diese Bits (Read only) gelesen
werden können.
> Nachteil könnte sein, dass man m.E. weder beim 8051 noch beim VIC> rauskriegt, auf welchem priority level man grad läuft
Ich überlege gerade, wozu man das brauchen könnte.
Wenn Du von einem bestimmten Vektor kommst, kannst Du aber in dessen
Prioritätsbits nachschauen, dann weißt Du die gerade laufende Priorität.
> Ein explizites Sperren aller interrupts> unter einem bestimmten priority level existiert nicht.
Auch da weiß ich nicht, wozu man das brauchen könnte.
Peter