Ich möchte euch ein sehr merkwürdiges Problem schildern, welches bei
Optimierungen > O0 auftritt:
Eine Timer-ISR ist so konfiguriert, daß sie jede Millisekunde auslöst.
Ohne Optimierung funktioniert dies wie erwartet.
Beim Optimierungslevel O3 findet eine doppelte Auslösung statt (ca 400ns
nach der ersten)
Ich habe sehr lange gebraucht, um hierfür einen "Workaround" zu finden-
die letzte Anweisung in der ISR ist eine "ohne effekt", die jedoch dazu
führt, daß wieder das normale Verhalten eintritt , d.h. die ISR genau 1x
aufgerufen wird.
Zum Nachvollziehen eine kleine STM32-Applikation, die bei jedem IRQ
einen kurzen High-Puls am GPIO3 vollzieht, danach das Timerflag
rücksetzt.
Man kann mit einem Oszi 1 oder 2 Pulse sehen, wenn man mit O3
kompiliert: - 1 pulse wenn die letzte aktion aktiv ist: "TimeTick;" (no
effekt statement, welches aber den 2. pulse unterdrückt)
- 2 pulse wenn nach dem Rücksetzen des Statusregisters nichts mehr
kommt
Hierzu habe ich 2 Fragen und würde mich freuen, wenn ein STM32 Experte
seine Weisheit mit mir teilen würde:
1) Wodurch genau kommt der Fehler zustande - die Registerbelegungen des
Timers ist exakt identisch.
2) Kann es sich um einen GCC Optimierungsfehler handeln? (Timingfehler
, ein fehlendes NOP sozusagen, welches ich mit dem "no return
statement" erzwinge?)
=> falls es in die Richtung geht, verstehe ich eines nicht:
ob mit oder ohne TimeTick; , die Größe des erzeugten Bin-Files ist
identisch. (da ich im ASM nicht soo fit bin, habe ich hier noch kein
Vergleich durchgeführt, werde dies morgen mal genauer analysieren)
Vielen Dank im Voraus für jegliche Anregungen oder Ideen, die mich auf
des Rätsels Lösung bringen
Anmerkung: Nutzt man die eingebaute "rücksetz-Funktion" der STM periph.
libary, tritt dieses Verhalten ebenso nicht auf.
Da ich die Routine jedoch später im RAM laufen lassen muß, habe ich
jegliche Flash-routinen in den RAM "portiert" - und danach trat es
auf..
Das Update-Flag bei den STM32-Timern ist ein bisschen schräg. Verschiebe
die Anweisung mit dem TIM_IT_Update an den Anfang der ISR. Poste auch
mal die dissassembly der -O0 und -O3 Varianten, das ist das wichtigste
bei Optimierer-Problemen...
>Das Update-Flag bei den STM32-Timern ist ein bisschen schräg.
Man sollte die Interrupt Flags ganz am Anfang der ISR löschen.
Tut man es zu weit hinten, kann es passieren das die ISR verlassen
wird bevor das Flag wirklich zurückgesetzt wurde. Und schwupps
landet man ein zweites mal in der ISR.
Hier nun die beiden Assembly Listings der ISR - jeweils direkt vor
"Main" abgeschnitten..
Im direkten Vergleich bemerkt man einige unterschiede - es deutet alles
darauf hin, daß Eure Annahme richtig war, denn im "Workaround" ist vor
dem Rücksprung noch ein zusätzlicher Befehl -> damit vermutlich genug
Zeit das Register rückzusetzen bevor der INT verlassen wird.
Eine Kleinigkeit fiel mir noch auf - einmal ist hinter dem Rücksprung
noch ein NOP vorhanden, ein anderes mal fehlt es.
So ganz wird mir dessen Existenzgrund nicht klar - ist dies evtl ein
anderer "workaround" für den Kommando-Pipeliner, dessen Sequenz ja durch
den HW-INT irgendwo unterbrochen wurde und der nun zurückspringen muß? -
die Frage wäre nur, warum das NOP nur in einem der Fälle und nicht
generell?!?
Vielen Dank im Voraus für eure Zeit und eure Ratschläge!
ein dankbarer STM32-User
Euer Rat ist eindeutig Gold wert - Sowas gehört als Warnung ganz Dick
gedruckt ins Datenblatt :)
Danke für diesen Tipp!
Da der Thread nun eigentlich abgeschlossen ist, hätte ich eine
philosophische Frage:
Wie wäre dieser Fehler einzuordnen - müßte der Compiler bei
entsprechenden Optimierungen nicht ein zusätzliches NOP zur Sicherheit
einfügen? (-> da es ja eindeutig nur bei best. Optimierungen auftritt)
Oder ist die Ursache eher im Silikon zu suchen, da offensichtlich das
Löschen des Registers und Update des Interrupt-Handlers nicht zeitgleich
passieren, sodaß im "fehlerfall" die ISR im Anschluß direkt nochmal
aufgerufen wird?
Euch Beiden eine angenehme Restwoche und nochmal vielen Dank für eure
Mühe!
der immer wieder dazulernende
STM32-User
STM32-User schrieb im Beitrag #3372931:
> Wie wäre dieser Fehler einzuordnen - müßte der Compiler bei> entsprechenden Optimierungen nicht ein zusätzliches NOP zur Sicherheit> einfügen? (-> da es ja eindeutig nur bei best. Optimierungen auftritt)
Das ist nun aber definitiv nicht Sache des Compilers. Das ist auch kein
Fehler, weder beim Compiler noch in der Hardware. Sondern das ist
einfach nur etwas, auf das der Programmierer achten muss.
Wenn du AVRs kennst, dann kennst du vielleicht den Effekt, dass ein
Input-Befehl, der unmittelbar auf einen Output-Befehl folgt, nicht
sicher den Zustand eines dadurch geänderten Pins erfassen kann. Weil im
Ablauf das eine oder andere taktgesteuerte Flipflop dazwischen sitzt.
> Oder ist die Ursache eher im Silikon zu suchen, da offensichtlich das> Löschen des Registers und Update des Interrupt-Handlers nicht zeitgleich> passieren, sodaß im "fehlerfall" die ISR im Anschluß direkt nochmal> aufgerufen wird?
Aufgrund des exakten Ablaufs in der Hardware ist es völlig normal, dass
der Zustand des Interrupt-Systems ein ganz klein wenig dem vom
Programmierer wahrgenommenen Programmablauf hinterher hinkt. ARM hatte
beim Design der Cortex-M die Interrupt-Verarbeitung durch Prozessors
aber sehr effizient gestaltet. Und hat damit nun manchen Programmier
überholt. ;-)
Bei anderen Architekturen landen in Interrupt-Handlern meist noch
diverse Befehle zwischen der letzten C Anweisung und dem Return. Dieser
Wasserkopf in Interrupt-Handlern sorgt üblicherweise dafür, dass dein
Problem nicht auftritt. Bei den Cortex-M gibts den aber nicht, folglich
muss man selber ein wenig aufpassen.
A. K. schrieb:>> Wie wäre dieser Fehler einzuordnen - müßte der Compiler bei>> entsprechenden Optimierungen nicht ein zusätzliches NOP zur Sicherheit>> einfügen? (-> da es ja eindeutig nur bei best. Optimierungen auftritt)>> Das ist nun aber definitiv nicht Sache des Compilers. Das ist auch kein> Fehler, weder beim Compiler noch in der Hardware. Sondern das ist> einfach nur etwas, auf das der Programmierer achten muss.>> Wenn du AVRs kennst, dann kennst du vielleicht den Effekt, dass ein> Input-Befehl, der unmittelbar auf einen Output-Befehl folgt, nicht> sicher den Zustand eines dadurch geänderten Pins erfassen kann. Weil im> Ablauf das eine oder andere taktgesteuerte Flipflop dazwischen sitzt
Danke für die Einordnung - wenn dies definitiv ein "Programmierer muß
das Wissen"-Ding ist, dann frage ich mich, wieso dies nicht in der
Beschreibung des Systemverhaltens (==> in der Doku/Datenblatt)
beschrieben wird, sondern selbst in den Beispielen oft ganz zum Schluß
as Flag gesetzt wird, was dem Nutzer extrem schwierig zu debuggende
Fehler unterschiebt.
Ich habe nun dazugelernt, frage mich aber wirklich, wie weit dieses
Wissen wohl verbreitet ist und wieviel Prozent der Projekte einfach nur
"Glück in Ihrer Unwissenheit haben", da sie längere Funktionen am ende
des ISRs haben.
Erlaube mir deshalb folgende Behauptung:
Wenn syntaktisch richtiger Code in einer Hochsprache übersetzt wird,
sollte dies unabhängig von der Optimierung zu gleichem Systemverhalten
führen.
Ist diese Annahme etwa zu naiv?
Gibt es einen allgemeingültigen "Sicheren Weg"/Tutorial/Guideline,
solche Probleme zu vermeiden?
(diesmal trat der fehler am Anfang der Entwicklung auf, es war daher
noch halbwegs übersichtlich. Tritt soetwas später und sporadisch auf,
ist das u.U. Projektgefährdend, da man irsinnig viel Zeit darauf
verwendet bis man einen solchen Fehler überhaupt reproduzierbar und
damit analysierbar kriegt.)
Die "IO-Problematik" bei AVRs kenne ich, stufte sie jedoch als absolut
irrelevant ein, da meiner Ansicht nach keine Notwendigkeit besteht,
sofort nach dem setzen eines Outputs Pins dessen input-register zu
lesen:
1. I.D.R hat man den outputwert ohnehin in irgendeiner Variablen
2. kann man ja auch das output register nehmen, welches (sofern mich
meine Erinnerung nicht täuscht) immer den korrekten Wert enthält
Für mein Verständnis sind es 2 paar Schuhe, ob ein IO regelmäßig falsch
gelesen wird oder eine Interuptroutine ohne erkennbaren und debugbaren
Grund 2x hintereinander ausgeführt wird.
Ohne ein schnelles Oszi und einige Stunden Sucherei ist der Fehler IMHO
gar nicht zu finden, (sofern er einen ohne das nötige Vorwissen ereilt)
Durch Eure Beiträge habe ich nun wieder etwas dazugelernt und stehe
deshalb tief in Eurer Schuld - vielen Dank!
Über jegliche Tipps, wie ich (und jeder spätere Leser dieses Threads)
solche oder ähnliche Fallstricke gezielt vermeiden kann, würde ich mich
natürlich sehr freuen.
STM32-User schrieb im Beitrag #3373167:
> Wenn syntaktisch richtiger Code in einer Hochsprache übersetzt wird,> sollte dies unabhängig von der Optimierung zu gleichem Systemverhalten> führen. Ist diese Annahme etwa zu naiv?
Nur bezogen auf jene Aspekte, die zur Sprache C gehören. Das
Laufzeitverhalten von Interrupt-Systemen oder I/O-Bausteinen in den
erzeugten Code einzubeziehen ist jedoch eindeutig nicht Sache des
Compilers.
STM32-User schrieb im Beitrag #3373167:
> Die "IO-Problematik" bei AVRs kenne ich, stufte sie jedoch als absolut> irrelevant ein, da meiner Ansicht nach keine Notwendigkeit besteht,> sofort nach dem setzen eines Outputs Pins dessen input-register zu> lesen:
Das ist ziemlich direkt zur hiesigen Situation vergleichbar. Es wird ein
Bit in einer I/O-Komponente zurückgesetzt, nämlich die Interrupt-Leitung
des Timers, und anschliessend über den Interrupt-Controller vom
Prozessor als Teil des Return-Befehls wieder ausgelesen.
Ein Beispiel, wo eine solche Sequenz bei AVRs leicht vorkommen kann, ist
die Abfrage einer Tastaturmatrix. Es muss ja nicht das gleiche Portbit
sein, um das es geht. Wenn man da nicht explizit ein NOP einschiebt,
dann kriegt man bei den ATmegas Probleme, die man bei den alten
AT890Sxxxx mangels Sync-Register am Eingang nicht hatte. Sollte avr-gcc
vor jeden Lesebefehl ein NOP einschieben?
PS: Der ARM Compiler weiss bei den Cortex-M überhaupt nicht, ob er es
mit einer Interrupt-Routine zu tun hat oder mit einer normalen Funktion.
Anders als bei anderen Architekturen üblich sind die aus dessen Sicht
identisch.
OK - wenn dem so ist, dann bin ich wohl dem falschen Pferd aufgesessen,
daß sich der Compiler für eine bestimmte Hardwarearchitektur drum
kümmern sollte
(könnte ich dies entscheiden, würde ich es definitiv als "must have" für
alle Compiler festlegen - warum? Effizienz, Robustheit, standardisiertes
Verhalten - eben die gründe, für die man eine Hochsprache i.d.R
einsetzt)
gibt es sowas wie eine Wissensdatenbank ala "für Proz.X füge n
waitstates ein oder mache es am besten so... , für Proz.Y ists egal..."
?
OT: manchmal wünsche ich mir die gute alte AVR-Assembler Zeit zurück...
Ok, die programme von damals hatten weniger Funktionalität, doch ob Ihr
es glaubt oder nicht, damals dauerte es zwar etwas länger bis ein
Programm fertig war, dafür hatte es aber auch WESENTLICH weniger Fehler
und lief ungleich Stabiler.
Kleine Anmerkung: ob ISR oder nicht müßte der Compiler herauskriegen
können.
OK - nennen wir es nicht Compiler, nennen wir es "Präprozessor ähnlicher
Optimierer / graue Haare Verhinderer" :)
Danke für Deine Ausführung. Verstehe jetzt was Du meinst, überlege mir
nur gerade, wie man soetwas durch standardisierte Prozesse in einem
Unternehmen verhindern könnte..
STM32-User schrieb im Beitrag #3373167:
> Erlaube mir deshalb folgende Behauptung:> Wenn syntaktisch richtiger Code in einer Hochsprache übersetzt wird,> sollte dies unabhängig von der Optimierung zu gleichem Systemverhalten> führen.> Ist diese Annahme etwa zu naiv?
Ja, das ist absolut zu naiv. Die Interruptflags gehören zum Registersatz
der Peripherie und nicht zum Cortex-Kern. Wie schnell die über die
internen Busse angebunden sind kann der Compiler nicht wissen. Wenn das
Zeitverhalten bei allen Optimierungsstufen gleich wäre, wozu wird da
überhaupt optimiert? Also das Verhalten was du hier erlebt hast wurde
hier schon beschrieben. Irgendwann fällt jeder mal auf sowas rein.
Man muss bei den Cortexen immer im Hinterkopf haben, dass die gesamte
Peripherie über die AHB-Busse angebunden ist. Teilweise kann deren
Taktrate auch noch deutlich nach unten geschraubt werden. Das würde die
Effekte noch verstärken. Eventuell kannst du ja das Register nochmal
lesen nach dem Schreiben. Ich denke an der Stelle wartet dann die CPU
bis das Register gelesen ist. Das muss man dann aber auch so geschickt
machen dass der Compiler das nicht wegoptimiert.
STM32-User schrieb im Beitrag #3373206:
> Kleine Anmerkung: ob ISR oder nicht müßte der Compiler herauskriegen> können.
Bei klassischen ARMs ist das so, weil ISRs einen gänzlich anderen
Wasserkopf haben als normale Funktionen. Nicht aber bei den Cortex-M.
> OK - nennen wir es nicht Compiler, nennen wir es "Präprozessor ähnlicher> Optimierer / graue Haare Verhinderer" :)
Ja, den Do-What-I-Mean-Not-What-I-Say Compiler hätten viele gerne. C ist
halt seit jeher andersrum konzipiert. :-)
obscurity schrieb:> Das muss man dann aber auch so geschickt> machen dass der Compiler das nicht wegoptimiert.
Wird er nicht, weil volatile. Der Prozessor auch nicht, weil in einem
Adressraum mit klarer Zugriffsreihenfolge (memory ordering).
obscurity schrieb:> Man muss bei den Cortexen immer im Hinterkopf haben, dass die gesamte> Peripherie über die AHB-Busse angebunden ist.
Eher die APB Busse. Der AHB ist der schnelle vorne, die oft mehreren
APBs sind die teils langsameren weiter draussen.
A. K. schrieb:> Ja, den Do-What-I-Mean-Not-What-I-Say Compiler hätten viele gerne. C ist> halt seit jeher andersrum konzipiert. :-)
Ich frage mich was das mit C und dem Compiler zu tun hat. Das selbe
würde dir im Assembler passieren. Der Hersteller der CPU und nicht ARM
steht vor der Entscheidung ob immer und überall oder nur bei bestimmten
Registern Waitstates eingeschoben werden bis zum wirklichen Updates der
Register. Hat er in deinem Fall aber nicht. Das würde ja auch die eine
oder andere Benchmarkzahl verschlechtern und das ist ein NoGo. Sicher
wird das auch in 99,9..% nicht benötigt. Und der Rest fällt einmal
darauf rein und dann nie wieder.
Der Compiler weiss weder ob dein Programm im Flash oder im Ram läuft und
ob du die Vektoren zur Laufzeit änderst oder nicht. An was soll er das
denn festmachen? Und soll er jedesmal 10ms Waitstates als Vorsorge
einschieben nur weil einer auf die Idee kommen könnte den AHB-Takt auf
10khz zu drehen um Strom zu sparen?
A. K. schrieb:> Eher die APB Busse. Der AHB ist der schnelle vorne, die oft mehreren> APBs sind die teils langsameren weiter draussen.
Ja klar. Wenn man es genau nimmt hast du natürlich Recht. Ändert aber
nichts an der Tatsache, dass vom Setzten eines Registers in der CPU bis
zu dem Zeitpunkt an dem das Register wirklich gesetzt ist immer eine
Zeit x vergeht die der Compiler nicht kalkulieren kann.
A. K. schrieb:> Wird er nicht, weil volatile. Der Prozessor auch nicht, weil in einem> Adressraum mit klarer Zugriffsreihenfolge (memory ordering).
Wenn es so ist, dann ist es ja gut. Ich wollte mich hier nicht soweit
aus dem Fenster lehnen und das ungetestet für alle Optimierungen zu
behaupten. Fakt ist, bei mir hat das nachträgliche Lesen des Registers
geholfen. Ob es aber nur an der zusätzlichen Zeit des Befehls lag oder
ob da Waitstates eingeschoben werden kann ich nicht mit Gewissheit
sagen.
A. K. schrieb:>> Kleine Anmerkung: ob ISR oder nicht müßte der Compiler herauskriegen>> können.>> Bei klassischen ARMs ist das so, weil ISRs einen gänzlich anderen> Wasserkopf haben als normale Funktionen. Nicht aber bei den Cortex-M.
Achso? - Ich dachte durch die ISR Sprungtabelle, die ja direkt auf die
betreffenden Kandidaten zeigt, kriegt ein Compiler dies raus...
Aber ich kenne mich zu wenig mit den Internas der Compiler aus -
vermutlich wäre dies zu hardwarespezifisch oder zu komplex, auf einzelne
Register-setzen Befehle zu schauen (ISR-Tabellen-Vektor ist ja im
Register gehalten und könnte sich gemeinerweise noch zur Laufzeit
ändern)..
So langsam ergibt alles einen Sinn :)
Der Traum vom "do what I mean not what I say" compiler ist leider viel
zu schön um wahr zu sein :)..
Als Alter Realist wäre ich trotzdem wenigstens für ein "behave like I
said" (without assuming I have in depth hardware timing knowledge)
compiler.
Vermutlich gibt es die sogar für teuer Geld von Keil&Co zu kaufen..
Schön, hier ein Forum zu haben, in dem es nach wie vor konstruktive
Leute mit Ahnung gibt.
OT: mal eine Frage - wie haltet ihr es eigentlich aus, bei dauerhaft
">20 Einträge pro Tag" die sinnvollen herauszufiltern und diese auch
noch kompetent zu beantworten?
Ich würde vermutlich nach einigen Wochen einfach keinen Bock mehr auf
das zehntausendste "welcher 8 bitter ist von allen der beste" Themen
haben, wohingegen eine (1!) Übersicht über Eignung und Anwendungsfälle,
gegenübergestellte Vor- und Nachteile ohne Frage äußerst interessant
wäre.
Also vor eurer Einstellung und euer Durchhaltevermögen empfinde ich
wirklich tiefen Respekt - Hut ab!!
obscurity schrieb:> Ja, das ist absolut zu naiv. Die Interruptflags gehören zum Registersatz> der Peripherie und nicht zum Cortex-Kern. Wie schnell die über die> internen Busse angebunden sind kann der Compiler nicht wissen. Wenn das> Zeitverhalten bei allen Optimierungsstufen gleich wäre, wozu wird da> überhaupt optimiert?
Danke für den Hinweis - jetzt weiß ich wenigstens woher die Probleme
kommen
Ich dachte, da der NVIC doch jetzt angeblich im Kern integriert wäre,
würden das Rücksetzen der ISR-Flags in der ISR schon via Hardware so
synchronisiert sein, daß der (viel zu schnelle kern) nicht gleich 2x
auslöst..
Ich muß zugeben, daß ich an eine zu langsame Synchronisierung von
Peripherie-Status-Registern" überhaupt nicht gedacht habe - ging
blauäugig davon aus, daß diese intern synchronisiert werden "bevor
irgendwas böses passiert"
-> dafür hat mir meine Applikation halt auch ein blaues Auge verpaßt :)
Ich finde allerdings, daß diese Info in den Peripheriebeschreibungen
fehlt- es ist einfach nicht oder wenn dann äußerst unzureichend erwähnt.
Beispielsweise eine Berechnung, wieviele Waitstates eingefügt werden
müssen wenn CPUTakt X und Peripherietakt Y ist.
Gibt es soetwas für STM32? - habe dergleichen noch nicht gesehen..
Wäre ja nötig um sicherzugehen, daß auch in zukünftigem Code (ggf. bei
veränderter Taktrate) alles sauber läuft.
STM32-User schrieb im Beitrag #3373298:
> Achso? - Ich dachte durch die ISR Sprungtabelle, die ja direkt auf die> betreffenden Kandidaten zeigt, kriegt ein Compiler dies raus...
Du solltest mal versuchen, die Ebenen von Compiler und Maschine
auseinander zu halten. Nicht jedes Array mit Zeigern auf Funktionen drin
ist eine ISR-Sprungleiste.
> Aber ich kenne mich zu wenig mit den Internas der Compiler aus -> vermutlich wäre dies zu hardwarespezifisch oder zu komplex, auf einzelne> Register-setzen Befehle zu schauen (ISR-Tabellen-Vektor ist ja im> Register gehalten und könnte sich gemeinerweise noch zur Laufzeit> ändern)..
Das kann man wohl sagen. Es hätte beispielsweise zur Folge, dass man für
jedes neue Registerbit eine neue Compilerversion bräuchte. Ein Alptraum.
> Vermutlich gibt es die sogar für teuer Geld von Keil&Co zu kaufen..
Woh kaum. Mit etwas Glück hast du bessere Doku und eine Liste gängiger
Fallstricke. C ist nach dem Motto "Schrott rein, Schrott raus" gebaut.
Egal ob GNU oder Keil.
> OT: mal eine Frage - wie haltet ihr es eigentlich aus, bei dauerhaft> ">20 Einträge pro Tag" die sinnvollen herauszufiltern und diese auch> noch kompetent zu beantworten?
Indem man sich über die anderen Fälle lustig macht. Ok, das sehen diese
Leute dann nicht immer so positiv. ;-)
A. K. schrieb:> Das> Laufzeitverhalten von Interrupt-Systemen oder I/O-Bausteinen in den> erzeugten Code einzubeziehen ist jedoch eindeutig nicht Sache des> Compilers.
Oder vielleicht doch? Z.B. AVR: Die Assembler-Instruktion 'sei' wird vor
einem letzten 'pop' eingefügt.
beim AVR wurden die "Peripherie-Stati" (die ja angenehmerweise im Kern
waren) automatisch zurückgesetzt.
- schön robust, gut durchdacht und ne Runde Sache meiner Meinung nach.
Wird echt zeit, daß Atmel oder irgendwer sonst mal wieder was nachlegt
in dieser Richtung :)
das "unschönste" am AVR waren immernoch die Klimmzüge, mit denen man im
Programm Konstante Daten aus dem Flash laden mußte und die Tatsache, daß
kein Code aus externem Speicher ausgeführt werden konnte.
(unschön in dem Sinne nur deshalb, weil progmem eben
Architekturspezifisch war und nicht mit einem "quasi-standard" zugriff
via const char * ptr ging.. )
Somit war der ganze mit progmem durchsetzte Code nicht mehr so einfach
portierbar...
Ralf G. schrieb:> Oder vielleicht doch? Z.B. AVR: Die Assembler-Instruktion 'sei' wird vor> einem letzten 'pop' eingefügt.
also würde in folgendem Code automatisch die Interrupts aktiviert
werden?
das wäre ja der Tod für jede größere ISR, da diese dann vorzeitig durch
andere interrupts unterbrochen werden könnte..
Woher hast du diese Info - oder habe ich etwas falsch verstanden?!?
....
cli
push R16
nop
pop R16
....
INTs nun aktiv ohne SEI ?
STM32-User schrieb im Beitrag #3373362:
> das wäre ja der Tod für jede größere ISR
nee, nee.
Meine Aussage bezog sich nur darauf, dass der Compiler sich nicht um
Hardware-Eigenheiten kümmert.
Der konkrete Sachverhalt wurde in einem älteren Thread mal erklärt. In
einem Programm von mir ist mir das mal aufgefallen. Ich weiss nicht mehr
in welchem Programm und in welchem Zusammenhang. Ich suche noch...
Ich glaube, mir fällt langsam wieder ein, was da gemacht wurde: Die
Wiederherstellung von SREG wurde um einen Befehl vorgezogen.
Ralf G. schrieb:> Oder vielleicht doch? Z.B. AVR: Die Assembler-Instruktion 'sei' wird vor> einem letzten 'pop' eingefügt.
Das Laufzeitverhalten des Prozessors selbst wird schon eingerechnet, so
wie es der Befehlsreferenz dokumentiert ist. Nicht Bestandteil davon ist
aber die Peripherie, wie NVIC, Timer etc.
Teil des dokumentierten Verhaltens ist, dass die ISR-Return Operation,
also ein BR LR mit entsprechender Pseudoadresse, anfangs den Zustand des
Interrupt-Requests abfragt, um daraus die optimierte Abarbeitung
weiterer Interrupts abzuleiten.
Irgendwelche Kopplungen zwischen Timer- oder NVIC-Registern und dem am
Core auflaufenden Interrupt-Signal gehören aber ebenso wenig dazu, wie
das exakte Zeitverhalten der von ST, TI oder NXP implementierten
Busssysteme.
Compiler sind Werkzeuge. Wie eine Axt. Es wäre wohl vermessen, den
Hersteller der Axt zu beauftragen, nur Axte erzustellen, die garantiert
keinen Finger abhacken können. Du musst schon selbst drauf achten, dass
keiner im Weg ist.
STM32-User schrieb im Beitrag #3373362:
> also würde in folgendem Code automatisch die Interrupts aktiviert> werden?
In eine ISR gehört kein SEI, es sein denn man will Schachtelung
erlauben.
Was vmtl. gemeint war: Der AVR Compiler erzeugt als Teil des Wasserkopfs
von Funktionen Code, der einen 16-Bit Stackpointer atomar ändert. Das
geht nur bei abgeschalteten Interrupts. Und darin findet SEI genau vor
dem letzten relevanten Befehl statt, was schon manche irritiert hat. Nur
wird bei bisher abgeschalteten Interrupts der Folgebefehl von SEI
immer direkt anschliessend ausgeführt.
Damit wird also eine bestimmte Eigenschaft des Prozessorkerns
ausgenutzt, um die Interrupt-Latenz zu minimieren.
Ralf G. schrieb:> Meine Aussage bezog sich nur darauf, dass der Compiler sich nicht um> Hardware-Eigenheiten kümmert.
Wenn du mit Mehrkern- und Mehrprozessorsystemen arbeitest, wie in
heutigen PCs oder Handys, und ein Programm mit mehreren Threads laufen
lässt, dann kriegst du je nach Prozessor und Problem noch ganz andere
Schweinereien auf den Tisch.
Dann musst du nämlich bei Nutzung gemeinsamer Variablen u.U. auch noch
selber drauf achten, dass diese Variablen in der richtigen Reihenfolge
im Speicher landen, weil sich das nicht nur durch Optimierung des
Compilers, sondern auch durch Optimierung innerhalb Prozessors anders
darstellen kann, als es aus dem C Quellcode ersichtlich ist.
Dann kann es sein, dass du manchen Code mit Memory-Barrier Befehlen
spicken musst, um diese Reihenfolge sauber zu ordnen. Auch das nimmt
einem der Compiler nicht ab. Weil sich der nicht um Code kümmert, den er
nicht wahrnimmt.
Den Optimierungs-Effekt der Hardware kannst du aber auch bei den
Cortexe-Ms erleben, zumindest theoretisch. Wenn du nämlich Daten ins RAM
schreibst, die anschliessend vom DMA Controller verwendet werden. Selbst
wenn du diese Daten als volatile deklarierst, ist dennoch je nach
Adressraumkonfiguration ohne entsprechenden Barrier-Befehl nicht völlig
gesichert, dass sie im RAM gelandet sind bevor der DMA Controller sie
dort rausfischt.
STM32-User schrieb:> dann frage ich mich, wieso dies nicht in der> Beschreibung des Systemverhaltens (==> in der Doku/Datenblatt)> beschrieben wird
Weil die Doku ST-Qualität hat. Schau mal
STM32: Errata vom STM32F4xx die nicht im Errata von ST stehen und
http://www.efton.sk/STM32/STM32F4xx_doc_errors.txt
Bei der Programmierung mit den STM32 stößt man leider ständig auf
fehlende/falsche/unzureichende Angaben in der Doku, da muss man sich mit
abfinden...
Und ich gebe meinen Vorrednern definitiv Recht, dass das einfügen von
Waitstates hier nicht Aufgabe des Compilers ist; bei richtigen™
Compilern wie dem GCC ist zB die ISR einfach nur ein Array an
Funktionspointern das per Linkerscript an die richtige Stelle gepackt
wird, eine ISR eine ordinäre C-Funktion, ein Hardware-Register einfach
nur eine Speicherstelle - der Compiler stellt einen Satz von Werkzeugen
bereit, mit denen man sich eine Anwendung zusammenbauen kann. Von der
Struktur des Ergebnisses weiß der Compiler nichts. Daher kann der
Compiler für eine Vielzahl an Controllern mit unterschiedlicher
Hardware, in verschiedenen Konfigurationen (Bootloader, RAM, Flash...)
benutzt werden. Hier passt es nicht hinein, dass der Compiler bestimmte
Register "erkennt" und für die anderen Code generiert. C/C++ ist eben
kein Rundum-Sorglos-Paket, und das ist auch gut so. Wenn du das willst
schau dir mal http://www.stm32java.com/ an ...
STM32-User schrieb im Beitrag #3373350:
> beim AVR wurden die "Peripherie-Stati" (die ja angenehmerweise im Kern> waren) automatisch zurückgesetzt.
Bei Baukastensystemen ist das wenig wahrscheinlich. Alle Microcontroller
auf ARM Basis sind genau das (ebenso PIC32 auf MIPS Basis). Der
Hersteller kombiniert einen eingekauften ARM Core mit allerlei
Peripherie-Modulen, die entweder er selbst entwickelte, oder wiederum
irgendwo einkaufte. Systeme aus einem Guss sind da nicht zu erwarten.
Gut, damit wäre das Problem eindeutig beim Hersteller des Chips zu
suchen - bzw. in dessen lausiger Art das Systemverhalten zu
dokumentieren.
Durch die immer schneller werdenden Produktzyklen hold man sich
automatisch als Kunde immer mehr Kinderkrankheiten ins Haus.
Ich Danke euch für eure Einschätzung und daß ihr meine Fehleinschätzung
"der compiler müßte es machen" korrigiert habt.
Damit rettet ihr mir - und hoffentlich vielen anderen Lesern dieses
Threads einige der kostbaren Finger.
Euch einen angenehmen Abend
STM32-User
ps:
wie gut daß es Backups gibt- sonst müßte ich diesen Beitrag vermutlich
mit der Nase verfassen - und selbst die wäre... Autsch :)
STM32-User schrieb im Beitrag #3373167:
> Die "IO-Problematik" bei AVRs kenne ich
Und sie ist auch im Datenblatt eindeutig dokumentiert:
"When reading back a software assigned pin value, a nop instruction must
be inserted as indicated in Figure 10-4 on page 51. The out instruction
sets the “SYNC LATCH” signal at the positive edge of the clock. In this
case, the delay tpd through the synchronizer is one system clock
period."