Hi Leute!
Ich habe ein merkwürdiges Problem in einem großen Projekt mit einem
ATxmega128a4u. Konnte das ganze jetzt aber stark eingrenzen.
Es geht darum, dass ich einen Timer Interrupt verwende um einen Pin zu
toggeln. Das C-Programm habe ich angehangen mit der markierten
Problemzeile in der Mainloop.
Kompilieren tu ich das Ganze mittels:
Nun ist das Resultat das folgende:
Ist die Problemzeile nicht auskommentiert, sehe ich auf dem Oszilloskop
einen zufälligen Flankenjitter von etwa 250ns an dem PORTE.3 Pin, der in
der ISR getoggelt wird. In dem großen Projekt hat es sogar für einen
Jitter von mehreren Mikrosekunden gereicht!
Die resultierenden lss Dateien habe ich ebenfalls angehangen und durch
ein diff laufen lassen. Neben den verschobenen Adressen ist der einzige
Unterschied der zusätzliche rcall, der "Func" aufruft. Ohne die Zeile
liegt der Jitter im einstelligen ns Bereich.
gcc version 4.7.2 (AVR_8_bit_GNU_Toolchain_3.4.2_939)
Interessantes Problem oder? Hat irgendjemand nur eine ganz leichte
Ahnung, was hier los ist? Im Moment stehe ich ein bisschen auf dem
Schlauch. Irgendwas in Richtung Stack Overflow? Buggy Compiler?
Viele Grüße und Vielen Dank schon mal für irgendwelche Tipps.
Es wird noch besser. Ein Versuch mit der avr toolchain 3.4.5 inlined
sogar den Funktionsaufruf (Obwohl mit fno-inline-small-functions
verboten - GRML), und das Problem besteht weiterhin! Durch einfaches
Kommentieren der Problemzeile kann man den Jitter am Pin ein und
ausschalten.
Siehe Anhang.
Der Prozessor beendet den aktuell laufenden Befehl, bevor er in die ISR
springt. Wenn der Interrupt während des nop kommt, gibt es max. 1 Zyklus
Verzögerung, kommt er dagegen während eines ret, können es auch 4 oder 5
werden. (ich schau jetzt nicht extra nach).
Kannst ja mal mit deinem Prozessortakt nachrechen, ob das hinkommt.
Wenn du jitterfreies Pinwackeln haben willst, nimm die Hardware-PWM.
Oliver
eingast schrieb:> Vielleicht macht ja die Funktion in <Problemzeile> das Problem.
Die Funktion enthält nur ein nop. Der von mir angehängte Quellcode ist
komplett und kann so kompiliert werden.
Wie auch in meinem zweiten Post ersichtlich, reicht in der Problemzeile
auch ein einfaches nop aus, um Jitter auf dem Pin zu erzeugen.
Oliver S. schrieb:> Der Prozessor beendet den aktuell laufenden Befehl, bevor er in> die ISR> springt. Wenn der Interrupt während des nop kommt, gibt es max. 1 Zyklus> Verzögerung, kommt er dagegen während eines ret, können es auch 4 oder 5> werden. (ich schau jetzt nicht extra nach).
Hm, da hast du natürlich schon Recht. Es kommt ja auch ungefähr hin bei
meinem Testprogramm.
In meinem großen Projekt sind es jedoch, wie gesagt schon Mikrosekunden
Jitter, die dort erzeugt werden. Und in einer Mikrosekunde vergehen dann
schon locker zig Takte, wo ich mich frage, was der Prozessor da in der
Zeit macht?
Und das, obwohl nirgendwo Interrupts gesperrt werden und das der Einzige
Interrupt mit HI Prio ist. Das habe ich im Disassemblat nachgeschaut.
Das ist wirklich merkwürdig.
Oliver S. schrieb:> Wenn du jitterfreies Pinwackeln haben willst, nimm die Hardware-PWM.
Ist schon klar, dass das Pinwackeln nur zur Demonstration dienen soll...
Oliver S. schrieb:> Simon K. schrieb:>> Das ist wirklich merkwürdig.>> sleep?>> Oliver
Leider nicht. Sobald ich den LUFA USB Stack in der Mainloop mitlaufen
lasse, steigt der Jitter auf etwa 1µs an. Das wären um die 30
Prozessortakte und das halte ich für zu viel.
Wie gesagt, habe den Code schon durchforstet, das geht bei dem LUFA
Stack sehr gut. Allerdings habe ich nichts gefunden, was die Verzögerung
von bis zu 30 Takten hervorrufen könnte. Interrupts werden definitiv
nicht deaktiviert im normalen Betrieb (nur während der Enumeration
kurzzeitig um die interne ID mittels NVM auszulesen). Und das Maximum,
was durch die Opcodes hervorgerufen wird sind 5 Taktzyklen, also einiges
unterhalb der zu beobachtenden Verzögerung.
Jetzt bin ich doch sehr ratlos. Das USB Modul funktioniert mit einem
asynchronen 48MHz Takt. Gibt es hier vielleicht Verzögerungen beim
Zugriff auf USB Register?
Simon K. schrieb:> LUFA USB Stack
Hm. Hab ich mir mal eben spaßeshalber runtergeladen.
Die lib scheint interruptbasiert zu sein. Bist du sicher, daß dein
Timerinterrupt die USB-Interrupts unterbrechen kann (und auch dürfte)?
Oliver
Oliver S. schrieb:> Simon K. schrieb:>> LUFA USB Stack>> Hm. Hab ich mir mal eben spaßeshalber runtergeladen.> Die lib scheint interruptbasiert zu sein. Bist du sicher, daß dein> Timerinterrupt die USB-Interrupts unterbrechen kann (und auch dürfte)?>> Oliver
Danke für deine Hilfe!
Glücklicherweise kann man die Interruptpriorität für den Stack
konfigurieren und die ist auf Medium eingestellt.
Darüberhinaus ist für die XMEGA Implementierung allerdings nur der
BusEvent Handler implementiert, der Rest wird gepollt (Known Issues).
Deswegen macht mich das Ganze so stutzig.
@ Simon K. (simon) Benutzerseite
>Leider nicht. Sobald ich den LUFA USB Stack in der Mainloop mitlaufen>lasse, steigt der Jitter auf etwa 1µs an. Das wären um die 30>Prozessortakte und das halte ich für zu viel.
Dann gibt es möglicherweise atomare Sequenzen, die kurzzeitig die
Interrupts sperren.
>von bis zu 30 Takten hervorrufen könnte. Interrupts werden definitiv>nicht deaktiviert im normalen Betrieb (nur während der Enumeration>kurzzeitig um die interne ID mittels NVM auszulesen).
Du hst nur bis jetzt keine gefunden. ;-)
>Jetzt bin ich doch sehr ratlos. Das USB Modul funktioniert mit einem>asynchronen 48MHz Takt. Gibt es hier vielleicht Verzögerungen beim>Zugriff auf USB Register?
Verschachtelte Interrupts?
Falk Brunner schrieb:> @ Simon K. (simon) Benutzerseite>>>Leider nicht. Sobald ich den LUFA USB Stack in der Mainloop mitlaufen>>lasse, steigt der Jitter auf etwa 1µs an. Das wären um die 30>>Prozessortakte und das halte ich für zu viel.>> Dann gibt es möglicherweise atomare Sequenzen, die kurzzeitig die> Interrupts sperren.
Gibt es, z.B. bei der Enumeration. Die geschieht aber nur einmal.
>>von bis zu 30 Takten hervorrufen könnte. Interrupts werden definitiv>>nicht deaktiviert im normalen Betrieb (nur während der Enumeration>>kurzzeitig um die interne ID mittels NVM auszulesen).>> Du hst nur bis jetzt keine gefunden. ;-)
Also mehr als im Disassemblat nach cli Instruktionen schauen, kann ich
nicht machen. Und die clis lassen sich an einer Hand abzählen und sind
alle nicht kritisch (werden z.B. nur während der Init ausgeführt).
>>Jetzt bin ich doch sehr ratlos. Das USB Modul funktioniert mit einem>>asynchronen 48MHz Takt. Gibt es hier vielleicht Verzögerungen beim>>Zugriff auf USB Register?>> Verschachtelte Interrupts?
Ich verwende ja einen ATxmega und eigentlich sollten jegliche USB
Interrupts (Medium Prio) von dem Hi-Prio Interrupt unterbrochen werden
können.
Dann werde ich mal mit der LED-Debug-Methode den Stack Stück für Stück
untersuchen.
@ Simon K. (simon) Benutzerseite
>Ich verwende ja einen ATxmega und eigentlich sollten jegliche USB>Interrupts (Medium Prio) von dem Hi-Prio Interrupt unterbrochen werden>können.
Das mag sein, aber auch in den Interrupts gibt es AFAIk kurze atomare
Sequenzen, wenn Speicher für die ISR auf dem Stack angelegt wird.
Simon K. schrieb:> Darüberhinaus ist für die XMEGA Implementierung allerdings nur der> BusEvent Handler implementiert, der Rest wird gepollt (Known Issues).
DAs kann ich nicht nachvollziehen. In den known issues steht nichts
davon, und auch das, was ich auf die schnelle im Code dazu finden
konnte, sieht nach Interruptnutzung aus.
Oliver
Oliver S. schrieb:> Simon K. schrieb:>> Darüberhinaus ist für die XMEGA Implementierung allerdings nur der>> BusEvent Handler implementiert, der Rest wird gepollt (Known Issues).>> DAs kann ich nicht nachvollziehen. In den known issues steht nichts> davon, und auch das, was ich auf die schnelle im Code dazu finden> konnte, sieht nach Interruptnutzung aus.
Komisch, ich meine es dort mal gelesen zu haben. Wie auch immer, es ist
nur der BUSEVENT Handler implementiert, wie man im Disassemblat sehen
kann:
1F4+0 -> BUSEVENT_vect
1F4+4 -> TRNCOMPL_vect
In den Busevent Handler habe ich mich schon mal reingesetzt und wie
erwartet, wird der nur bei der Enumeration aufgerufen (um die
Enumeration zu starten).
Jegliche Datenkommunikation wird in USB_USBTask() (Mainloop) gemacht.
Es muss also irgendwie damit zu tun haben.