Ich kann nicht ganz nachvollziehen wie auf einem STM32F302R8 (Cortex M4
mit FPU) eine ISR-Latenz von etwa 27 Takten mit der Compileroption
(-Ofast) und etwa 32 Takten mit der Option (-Os) zustandekommt (mit
arm-atollic-eabi-gcc).
Die Werte sind am Oszilloskop jeweils durch den Unterschied zwischen dem
Setzten eines Pins mit einer Output Compare Einheit (Hardware) und dem
entsprechenden Output-Compare-ISR, in welcher als erster Befehl ein
anderer Pin mit GPIOB->BSRR = LL_GPIO_PIN_13; gesetzt wird.
So wie ich es verstehe, sollte es auf einem Cortex M4 12 Takte dauern um
die CPU-Register auf den Stack zu sichern und in die ISR zu springen.
Wenn die FPU-Register zusätzlich gesichert werden müssten, kämen 17
Takte hinzu. Da ich keine FPU-Befehle in der ISR verwende würde ich
erwarten, dass es 12 Takte + die Ausführungszeit des Pin-setzt-Befehls
dauert, bis der Pin high wird. Das "Lazy Context Switching", bei welchem
die FPU-Register nur bei Bedarf gesichert werden ist nach meinem
Verständnis standardmäßig aktiv.
Weis jemand wie diese Latenz zustandekommt?
Oder sind Möglichkeiten bekannt wie das Sichern der FPU-Register
explizit unterbunden werden kann?
Mit welcher Frequenz läuft der Cortex? Wie wurde die Taktfrequenz des
AHB eingestellt? Sind bei den 12 Takten auch die Pipeline Latenz dabei?
Außerdem hat die ISR möglicherweise noch einen Prolog, Assemblercode?
Der interne Flash geht max bis 24 Mhz, d.h. es kommen noch zusätzliche
Latenztakte hinzu wenn der Code aus dem Flash läuft und die Taktfrequenz
höher ist.
Es sind 2 nichtsequentielle Flash-Zugriffe dabei, einer für den Vektor
und einer für die ISR. Die allerdings parallel zum Sichern der Register
erfolgen. Bei 48-72 MHz gibts pro nonseq access 2 Waitstates oben drauf.
Für taktgenaue Echtzeit finde ich die ARMs (mittlerweile) zu komplex.
Neben dem NVIC mit den ganzen Prioritäten gibt es da ja noch ein paar
weitere Punkte:
* single-cycle access aufs Flash geht nut bei niedrigen Takten,
wait-states=1 bzw. 2 nötig für höhere Taktraten. Kritische ISRs müssen
also ins SRAM.
(Das mit den waitstates ist durch das ganze HAL-Gebastle auch wieder
soweit wegabstrahiert dass ein 0815-Entwickler das gar nicht mehr
mitbekommt wenn er nicht gezielt tiefer gräbt.)
* Mit den ganzen Bussen (AHBs, APBs) habe ich nicht das Gefühl da noch
den Überblick über mögliche blocking points zu haben wenn da z.B. gerade
irgendwo ein DMA-burst passiert.
Und wahrscheinlich gibt es da noch mehr Fallstricke.
armel aus dem ice schrieb:> * single-cycle access aufs Flash geht nut bei niedrigen Takten,> wait-states=1 bzw. 2 nötig für höhere Taktraten. Kritische ISRs müssen> also ins SRAM.
... um damit die Latenz zu vergrössern, statt sie zu reduzieren: "The 12
cycle latency requires that a nine cycle stack push can take place on
one interface (typically the System interface) in parallel with a six
cycle vector table read and interrupt handler fetch on other interfaces
(typically I-Code). If these operations cannot be performed in parallel,
they will have to be performed one after the other, increasing the
latency."
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka16366.html
> anderer Pin mit GPIOB->BSRR = LL_GPIO_PIN_13; gesetzt wird.
Bis das Signal am Pin zu messen ist, dürften etliche (CPU!!!-) Takte
verstreichen. Diese muss man von der Latenz natürlich abziehen.
Denn Latenz heißt ja: Zeit von Eintreffen des Interrupts (wo genau, wenn
noch ein paar Synchronisationsstufen durchlaufen werden???) bis zum
Beginn des ersten Befehl in der ISR.
Versuch dazu: Diesen Pin in einer Endlosschleife togglen und dann messen
...
A. B. schrieb:> Versuch dazu: Diesen Pin in einer Endlosschleife togglen und dann messen
Wenn damit die Latenz ermittelt werden soll, dann setzt dies voraus,
dass der Befehl erst beendet wird, wenn der Pin den Zustand gewechselt
hat. Das muss aber nicht zwangsläufig der Fall sein.
Ein Gast schrieb:> GPIOB->BSRR = LL_GPIO_PIN_13;
Das sind mindestens 3 Assembler Befehle, auch wenn es nur eine Zeile in
C ist:
1
LDR R0, [PC+X] ; Register Addresse laden
2
MOV R1, LL_GPIO_PIN_13
3
STR [R0], R1
Der erste LDR braucht mindestens 2 Takte, bei 2 Flash Waitstates eher 4.
Der abschließende STR braucht mindestens 2 und arbeitet mit einem
Schreibpuffer.
Je nach Anbindung der GPIOs (AHB oder APB) kann der eigentliche
Hardwarezugriff noch weitere Takte kosten - APBs laufen mitunter nicht
auf vollem Takt.
Der OP könnte mal -mslow-flash-data probieren, falls die GCC Version neu
genug ist. Bringt aber IMHO höchstens 2 Takte hier.
Jim M. schrieb:> Der erste LDR braucht mindestens 2 Takte, bei 2 Flash Waitstates eher 4.
Bei Optimierung auf Tempo sollte die Adresse nicht per Flash-Load,
sondern mit 2 16-Bit-Immediate Befehlen geladen werden (MOV,MOVT).
A. K. schrieb:> Wenn damit die Latenz ermittelt werden soll, dann setzt dies voraus,> dass der Befehl erst beendet wird, wenn der Pin den Zustand gewechselt> hat. Das muss aber nicht zwangsläufig der Fall sein.
Logisch. Genau genommen, müsste sogar das Ende des Befehls exakt mit dem
Wechsel des Pin-Zustandes zusammen fallen (weder früher noch später!).
Und eine Endlos-Schleife reicht genau genommen auch nicht, da einem der
Prefetch natürlich einen Strich durch die Rechnung macht. Also die
Schleife abrollen ...
Selbst der Zeitpunkt, wann die CPU oder der NVIC den Interrupt bekommt,
ist ja nicht präzise bekannt. Da der Timer wohl nicht mit dem CPU-Takt
läuft, sind zwischen Timer-Ausgang und NVIC auch wieder ein paar weitere
Stufen ...
Aber es ging schließlich nicht um eine exakte Messung der Latenz (die
ist von außen schwierig bis unmöglich, da so viele in den Brei spucken),
sondern nur um einen Plausibilitätsüberlegung, wo diese 27 im Vgl. zu
dem "Werbe-Slogan" 12 wohl herkommen mag.
A. K. schrieb:> Bei Optimierung auf Tempo sollte die Adresse nicht per Flash-Load,> sondern mit 2 16-Bit-Immediate Befehlen geladen werden (MOV,MOVT).
Auch als Pseudo-Befehl MOV32 verfügbar. Ist 8 Bytes lang, statt summarum
6 bei LDR, aber unabhängig von der Zugriffszeit vom Flash in 2 Takten
durch.
A. K. schrieb:> Bei Optimierung auf Tempo sollte die Adresse nicht per Flash-Load,> sondern mit 2 16-Bit-Immediate Befehlen geladen werden (MOV,MOVT).
Das gilt offenbar nicht mehr für neuere GCC Versionen. Hier wird
movw/movt nur noch mit -mslow-flash-data generiert, ansonsten nimmt er
LDR auch mit -O3 Optimierung.
So um GCC 4.5 herum hatte ich noch die movw/movt auch im normalen -O3
Disassembly gesehen.
Nochmal ein Update in der Sache:
Ich kann es jetzt soweit nachvollziehen wie die Latenz entsteht.
Wie dies hier bereits geäußert wurde, erzeugen die Waitstates des Flash
den unerwarteten Teil der Latenz. Ich hatte den Controller mit 64MHz
betrieben, sodass für den Flash, welcher mit maximal 24MHz betrieben
werden darf 2 Waitstates erforderlich werden. Dementsprechend kommt es
zu Verzögerungen, wenn die Befehle der ISR aus dem Flash geladen werden.
Testweise habe ich den Controller einmal mit 20MHz getaktet, sodass der
Flash mit 0 Waitstates betrieben werden kann. Dabei beträgt die
Verzögerung bis der erste Befehl in der ISR zur Ausführung kommt etwa
17Takte. Dies liegt etwa im Rahmen dessen, was ich mit einer ISR-Latenz
von 12Takten + Ausführung des Befehls erwarten würde.
Die "Lazy Context Switch"-Funktion ist auch standardmäßig definitiv
aktiv. Ich hatte mir die Bits im entsprechenden Register in der ISR
einmal angesehen.
Falls es interessiert, ist hier noch der C-Code der ISR und das
Disassembly.
Eventuell kann jemand der etwas mehr Ahnung von Assembler hat, das
Entstehen der verbleibenden Differenz zu den 12 Takten erklären.
Was mich im Disassembly auf den ersten Blick etwas verwundert ist, dass
die Überprüfung ob das CC4IF-Flag gesetzt ist, anscheinend zum Teil vor
dem Befehl zum setzten des Pins ausgeführt wird.
17/27 Takte bis zum Anfang der ISR, oder bis der Pin wackelt?
Beim STM32 gibts einen kleinen Schmutzeffekt, der die Zählung von Takten
etwas erschwert. Die haben das Flash nämlich ziemlich auf Kante genäht.
Das ist bloss 64 Bits breit, liefert die bei 2 Waitstates aber nur alle
3 Takte ab. Bei in 32 Bits codierten Befehlen kann das knapp werden.
Weshalb auch die -mslow-flash-data Option u.U. nur ein Problem durch ein
anderes ersetzt.
Dass der zweite LDR vorgezogen wurde kann eine Pipeline-Optimierung
darstellen. Das Ergebnis eines Load-Befehls ist bei vielen CPUs nicht
ohne Verzögerung im nachfolgenden Befehl als Speicheradresse verwendbar
(address generation interlock). Weshalb ein cleverer Compiler versucht
sein kann, die Befehle so anzuordnen, dass dies nicht auftritt.
A. K. schrieb:> Welche µCs erlauben eine auf den einzelnen Takt genau reproduzierbare> ISR-Latenz?
- 8051 durch Registerbänke (bei Ausführung im RAM oder Flash ohne
Waitstates)
- Cortex-R durch FIQ Registerbank
> PS: Grad drüber gestolpert: "Zero jitter support on Cortex-M0/Cortex-M0+
Das soll nur für eine reproduzierbare Latenz sorgen, was aber für
"normale" Echtzeit ausreicht.
Cortex-M sind wegen ISR-Autostacking grundsätzlich langsam. Das ist der
Preis für ISR in C ohne ASM-Wrapper und das Einsparen von
Registerbänken.
Lothar schrieb:> - 8051 durch Registerbänke (bei Ausführung im RAM oder Flash ohne> Waitstates)
Bei Intels Original geht m.W. die Befehlsausführungszeit in die Latenz
ein. Befehle unterschiedlicher Ausführungszeit führen also zu variabler
Interrupt-Latenz. Bei neueren Cores mag das anders sein.
>> PS: Grad drüber gestolpert: "Zero jitter support on Cortex-M0/Cortex-M0+>> Das soll nur für eine reproduzierbare Latenz sorgen, was aber für> "normale" Echtzeit ausreicht.
Eben. Ich schrieb deshalb ja auch "taktgenau". Das impliziert eine feste
Reaktionszeit, nicht unbedingt die kürzeste.
Ein Gast schrieb:> Jeweils zwischen dem auf High schalten eines Output-Compare-Pins und dem> Pin, welcher in der ISR des OC-Kanals auf High gesetzt wird.
Dann erklärt sich ja wohl der Unterschied von 12 zu 17 Takten fast von
selber.
Von den übrigen 10 Takten bei 2 Waitstates lassen sich 8 direkt durch
die Waitstates erklären, denn es gibt 4 nichtsequentielle Flashzugriffe.
Vektor, ISR-Start und die beiden LDRs. Bei den übrigen beiden Takten
könnte z.B. ein LDR durch einen laufenden Prefetch verzögert ausgeführt
werden.
Probier mal -mslow-flash-data bei Optimierung für Tempo.