Hallo ARM7-Gemeinde um Philips LPC2000, Atmel, etc...., Gibt es hier jemanden, der sich mit dem Thema "Spurious Interrupts" (Scheininterrupts) etwas auskennt? Das betrifft wohl hauptsächlich µC, in denen ein ARM7-Core mit dem VIC (Vectored Interrupt Controller PL190 von ARM) zusammen arbeitet. Im Philips Datenblatt gibt es dazu immerhin ganze 2 Seiten Erklärung, die mit denen auf der ARM Homepage übereinstimmen. Arbeite mit Keil Tools und bin gerade dabei, einen Spurious Interrupt Handler in Assembler zu schreiben und direkt in der Startup.s zu platzieren. Das kostet aber Interrupt-Latenzzeit. Keil schlägt vor, eine Default Interruptroutine zu installieren, zum Abfangen nicht identifizierbarer Interrupts. Kostet ebenfalls Ausführungszeit. Ist das jetzt mit Kanonen auf Spatzen geschossen? Neuerlich, gibt es dazu auch einige Threads in Diskussionsforen, z.B. im LPC2000-Forum bei Yahoo, und auch hier im Forum, die das Thema immer wieder aufgreifen. Laut Philips User Manual betrifft das hauptsächlich Fälle, in denen bei kernnaher Programmierung die Interrupt-Flags verändert werden. Und wenn ich das nicht tue, sondern Interrupts nur über VIC Register sperre und freigebe? Bin ich dann auch betroffen? Nun, ich erwarte von meiner sehr interruptlastigen Anwendung, daß sie vernünftig läuft. Messe Zeiten mit Timer Capture und einigen 10000 Interrupts pro Sekunde, das soll absolut zuverlässig sein. Nun, ich habe da verschiedene Ansätze. Möchte in meiner Anwendung auch die Technik der "Nested Interrupts" (unterbrechbarer Interrupt per Software, ist standardmäßig bei ARM nicht vorgesehen) anwenden. Und da muß man die Interruptflags direkt im Kern ändern. Als nächsten Schritt, möchte ich die Spurious Interrupts auch zählen, in einer festen RAM-Location. Hat da mal jemand etwas Erleuchtung? Gruß Dietmar
Philips AN10414 schon gelesen? "Das betrifft wohl hauptsächlich µC, in denen ein ARM7-Core mit dem VIC (Vectored Interrupt Controller PL190 von ARM) zusammen arbeitet." Das zugrundeliegende Problem betrifft alle ARM7 Prozessoren mit vektorisiertem Interrupt-Controller, egal welchen Typs. So auch Atmel SAM7. Ursache liegt darin, dass die Interrupt-Bearbeitung im Prozessor nicht exakt zeitgleich mit der Interrupt-Verarbeitung im Interrupt-Controller erfolgt.
"Und wenn ich das nicht tue, sondern Interrupts nur über VIC Register sperre und freigebe? Bin ich dann auch betroffen?" Ich denke ja. Das Problem tritt ja auf, wenn sich der VIC-Zustand ändert, nachdem der Prozessor den IRQ akzeptiert hat, aber bevor er den Vektor holt. Manipulation der Interrupt-Enables im CPSR spielen also keine Rolle (diese Phase ist schon durch), wohl aber alle Operationen, die Einfluss auf dem Vektor nehmen können. Wozu Interrupts-Enable-Flags an der Quelle und aber auch die Masken im VIC gehören. Ich würde empfehlen, den Spurious Interrupt Handler in jedem Fall zu implementieren. Der Versuch, sorgfältig um das Problem herum zu programmieren, mag heute funktionieren, reagiert aber sehr empfindlich auf spätere Änderungen.
Der VIC meldet den Interrupt eines Peripherals an den ARM-Core, ist klar. Natürlich, habe ich alle Application Notes durch. Aber auch um die AN10414 gab es mächtigen Aufruhr, weil keine vollständige Lösung und keine Linderung in Sicht (Nested Interrupts, etc.). Habe das Schaltbild des Vectored Interrupt Controllers meines µC (LPC2129) im User Manual. Einige Gatter und Multiplexer. Ist da irgendwas nicht richtig, was ich übersehe? Dort gibt es doch nur die beiden Meldeleitungen IRQ und FIQ. Parallel dazu, muß der VIC die Adressen und den Slot zuordnen, beim Interrupt den Vektor zurückgeben. Laut Beschreibung, gibt es manchmal keine Zuordnung zum Vektor. Ist hier was faul? Ist das ein Design- oder Timing- Fehler? Gruß Dietmar
A.K.: Danke für die Erklärung, da scheint die Arbeitsweise des VIC mit dem ARM-Kern nicht synchron. Also passiert das auch im "normalen" Betrieb, ohne auf den Kern zuzugreifen. Meine Anwendung ist mit einigen 10000 Interrupts pro Sekunde etwas interruptlastig. Mit Spurious-Handler läuft tatsächlich meine Messung präziser, das ist echt ein Ding !!! Simulieren oder debuggen kann man das ja leider nicht. Jetzt werde ich die Spurious Interrupts tatsächlich mal versuchen zu zählen. Also, den Default Interrupt muß ich ja dann auf jeden Fall installieren (Interrupt, aber Zuordnung unbekannt). Wenn ich das richtig verstanden habe, gibt es aber zum Spurious Interrupt immer auch den echten Interrupt, so daß nichts verloren geht? Bin aber auch für jeden weiteren Tipp sehr dankbar. Gruß Dietmar
"da scheint die Arbeitsweise des VIC mit dem ARM-Kern nicht synchron." Fairerweise muss man sagen, dass der Core hier unschuldig ist. Die Vektorisierung ist ja im Core nicht vorgesehen, sondern findet separat per Software statt. Daher ist das kaum vermeidbar. "Wenn ich das richtig verstanden habe, gibt es aber zum Spurious Interrupt immer auch den echten Interrupt, so daß nichts verloren geht?" Der Spurious Interrupt tritt ja nur dann auf, wenn dem VIC nichts passenderes einfällt. Wenn du also sämtliche realen Interrrupt-Quellen vektorisiert hast, muss im Default Handler bloss die üblichen IRQ-Quittung stehen. Etwas hässlich in der Philips Doku ist die mangelnde Unterscheidung zwischen den Spurious Interrupts einerseits und dem Latency Problem bei gleichzeitiger Abschaltung von IRQ und FIQ andererseits. Das sind aber zwei völlig verschiedene Probleme. Letzteres ist der radikalen Vereinfachung mit daraus folgender inkonsistenter Verarbeitung von Interrupts zu danken: Interrupt-Check vor/während der Ausführung eines Befehls, aber Ausführung vom Interrupt danach. Die meisten Architekturen implementieren Interrupts so, als ob die gesamte Interrupt-Verarbeitung konsequent zwischen zwei aufeinanderfolgenden Befehlen erfolgt (wie auch immer das intern realisiert sein mag). Ich neige dazu, auf softwareseitige Priorisierung zu verzichten. Schön ist's eh nicht, kostet Zeit und lässt sich meist durch alternative Verarbeitung von Events lösen. Für ganz eilige Dinge hat man den FIQ, und für den Rest sollte man im Interrupt nur das Nötigste tun, und den Rest der Hauptschleife oder einer RTOS-Task überlassen.
"Wenn ich das richtig verstanden habe, gibt es aber zum Spurious Interrupt immer auch den echten Interrupt, so daß nichts verloren geht?" Es scheint so. Mein Kollege macht das auch so und es scheint zu funktionieren. Die AN10414 von Philips macht aber die Interruptbehandlung doppelt, einmal im richtigen und einmal im Spurious Interrupt Handler. Das geht aber nur, wenn man nur eine einzige Interruptquelle hat (aber für so einfache Programme braucht man wiederum keinen ARM). Also ist diese AN10414 voll fürn Arsch. Warum solche derart wichtigen Sachen nicht mal klar und im Datenblatt beschrieben werden, ist mir unverständlich ! Und es hat auch nichts mit ungeschickter Programmierung zu tun, zeitweise Abschaltung von Interruptquellen ist immer dann ein Muß, wenn ein Hauptprogramm mit einem Interrupthandler kommuniziert, wie z.B. bei gepuffertem CAN, UART usw.. Sobald da ein Interrupt reinhaut (kritical section), sind die Daten ja nicht mehr konsistent. Und einsehen kann ich es auch nicht, warum nicht der richtige Vector angesprungen wird, man hat doch nur das Enable-Bit gelöscht, der Vector steht doch weiterhin da, wo er soll. Und wenn CPU und VIC nicht synchron sind, was solls, schlimmstenfalls wird der Interrupt trotz Sperre ausgeführt, wenn er schon in der Queue ist. Mit 1-2 NOPs hinter einem Disable könnte bestimmt jeder leben. Peter
"man hat doch nur das Enable-Bit gelöscht, der Vector steht doch weiterhin da, wo er soll." Was soll er denn machen: Den höchstpriorisierten Vektor aller abgeschalteten Interrupts liefern (stateless)? Oder immer den zuletzt ermittelten? Nope, mit Spurious Interrupts kann und muss man leben. Solche Tierchen sind auch ausserhalb des ARM Terrariums quicklebendig. Nur dass Intels 8259 keinen Default Vector hat, sondern anscheindend seelenruhig den jeweils höchstnummerierten abliefert (im PC also 7 oder 15, je nachdem welchen Controller man erwischt). Da finde ich die Philips-Variante sympathischer, wenngleich Atmel netterweise sogar einen eigenen Vektor dafür vorsieht während Philips das mit unvektorierten in den gleichen Topf schmeisst.
Hallo! Aber ich finde, wenn man jetzt jeden IRQ, der auftreten kann in der Standard-IRQ-Routine als spurious IRQ abfangen muss, dann finde ich diese vektorisierten IRQs total umsonst. Dann muss man ja alles doppelt abfragen und abfangen und über die kleinen, älteren PICs schimpfen die meisten Leute, weil diese nur einen IRQ-Vector besitzen. Dann wäre es doch besser, wenn ich gleich nur die Standard-IRQ-Routine nehme, denn dann habe ich keine Probleme mehr mit Spurious-IRQs, denn dann fällt sowieso alles in einen Topf und ich brauch nichts doppelt zu programmieren. Sehe ich das richtig? Auch wenn solche Fehler bei Intel-Prozessoren anscheinend auch zum Standard gehören, finde ich es nicht richtig, den Programmierer für derart architektonische Nachteile büßen zu lassen, sodass dieser durch programmtechnische Meisterleistungen glänzen muss, damit das Ding endlich halbwegs korrekt läuft. Der Programmierer hat sowieso schon mit der Realisierung der Anwendung zu tun. Gruß, Martin
"wenn man jetzt jeden IRQ, der auftreten kann in der Standard-IRQ-Routine als spurious IRQ abfangen muss" Musst du doch nicht. Wenn ein Interrupt vektorisiert und aktiv ist, kommt der Vektor durch. Den Default-Vektor liefert der VIC nur, wenn der Interrupt mal kurz anlag, es aber zum Zeitpunkt des Vektorzugriffs nicht mehr tut. In welchem Fall dich der Interrupt auch nicht interessieren muss. Im Default-Vektor-Handler müssen sich nur nicht-vektorisierte Interrupt-Routinen befinden. Und er muss in jedem Fall definiert werden, auch wenn alle Interrupts vektorisiert sind. In diesem Fall besteht die Routine nur aus dem üblichen abschliessenden Tritt in den VIC und das war's.
"finde ich es nicht richtig, den Programmierer für derart architektonische Nachteile büßen zu lassen" Ohne jetzt den gesamten Überblick zu haben, stelle ich hier mal die These auf, dass jeder Controller mit vektorisierten Interrupts dieses Problem hat, wenn die Vektoren nicht integraler Bestandteil der Grundarchitektur sind. Grund: Nur wenn die Information zur Interrupt-Quelle und dem dafür auszuwählenden Vektor gleichzeitig mit der Interruptanforderung anliegt, tritt das Problem nicht auf. Sobald der Core nur weiss "Interrupt liegt an", nicht jedoch "welches Sprungziel" und dafür irgendwo anfragen muss, ist das Problem mit spurious Interrupts nur schwer zu vermeiden. Beispielsweise konnte das auch auf 68000 bei externer Vektorquelle auftreten, nicht jedoch bei interner Vektorquelle (Autovektoren).
PS: Die Dokumentation eines Problems ist nicht notwendigerweise als Nachteil des Produkts zu sehen! Schlimmer ist es, wenn das Problem vorhanden und bekannt ist, man aber ohne Unterschrift unter ein NDA (non disclosure agreement, also Maulkorb) nichts davon erfährt. Diese Erfahrung hat z.B. Intel beim berühmten Divisionfehler des Pentium-Classic gemacht, und daraus gelernt. Bis dahin waren bei den Prozessoren offiziell so gut wie nie irgendwelche Fehler bekannt, seither wimmelte es nur so von offiziell dokumentierten Fehlern.
Was wäre z.B., wenn man die Spurious-IRQ-Routine zwar anlegt, aber leer ließe? Würde der korrekte Vektor im Nachhinein vom µC nocheinmal gelesen und angesprungen werden oder nicht?
Es gibt keinen korrekten Interrupt-Vektor, weil es keinen Interrupt gibt. Es gab zwar zeitweilig mal einen, es gibt ihn aber nicht mehr. Da kein Interrupt vorliegt, passiert genau das was passieren soll: nichts. Der obligatorische abschliessende Tritt in den VIC (schreiben ins Vektorregister oder sowas in der Art, ich hab's hier grad nicht parat) sollte in der Routine schon drin sein.
Ich lese mich mal in diese Problematik hinein. Danke A.K. für deine guten Erklärungen. Was ich nicht ganz verstehe ist das Watchdog-Handling, welches im "AN10414 - Handling of spurious interrupts in the LPC2000" beschrieben wird. Dort steht über den Watchdog: The feed sequence of the watchdog cannot be interrupted by any interrupt source. ... So a typical feed sequence would take the following form: VICIntEnClr=0x... // Disabling interrupts in the VIC WDFEED=0xAA; // Feeding the Watchdog WDFEED=0x55; VICIntEnable=0x.. // Enabling interrupts in the VIC Ich verstehe nun nicht, warum hierbei mittels VICIntEnClr der IRQ ausgeschaltet wird, weil genau durch dieses Ausschalten und wieder Einschalten diese spurious-IRQs herbeigeführt werden. Wäre es dann nicht besser den IRQ mittel SWI-(Software Interrupt) zu blockieren, damit das nicht passieren kann? Danke. Tschüss, Martin
Ich will mal hoffen, das der Autor der AN genau weiss was er tut. Mir erscheint das etwas mutig. Nicht wegen des möglichen spurious-IRQs, denn der "verlorene" IRQ wird in diesem Fall später wieder nachgeholt. Aber diese Sequenz geht davon aus, dass die Abschaltung rechtzeitig kommt, um das zugrundeliegende Watchdog-Problem zu vermeiden, nämlich eine Unterbrechung zwischen den beiden FEEDs. Und das hängt vom exakten IRQ-Timing zwischen VIC und ARM7TDMI-Core und damit vielleicht sogar von Compiler-Optimierung ab. Mir ist bei einer Core-internen Abschaltung wohler, also per IRQ/FIQ-Bits im CPSR. Und spurious-IRQs kriegt man so auch nicht. Inwiefern SWI?
Mit diesen SWI-Interrupts ist es möglich alle IRQs zu sperren. Hierfür definiert man unter CARM z.B. eine Funktion. Auch ein entsprechendes swi_verc.s-File muss noch eingebunden werden. Sobald der µC aus der Funktion wieder rausspringt, sind alle IRQs wieder frei. void watchdog(void) __swi(0) { WDFEED=0xAA; WDFEED=0x55; } Dieses Thema wird unter dem Stichwort SWI in der Keil-Umgebung genau erklärt. Gruß, Martin
Ok, kenne Keil nicht. Ist da also ein SWI-Dispatcher drin? Sieht sinnvoller aus, als die Variante im AN. Allerdings auch langsamer, was aber in diesem Fall kaum stören dürfte.
Eine Frage habe ich noch: Kann ein als FIQ-definierter IRQ auch einen Spurious-Interrupt auslösen oder ist dies nicht möglich, da die FIQ-Routine auch mit einer Standard-Adresse angesprungen wird? Gruß, Martin
Kann nicht passieren, da das FIQ-Sprungziel üblicherweise nicht durch den VIC definiert wird. Das ist ja grad der von mir oben skizzierte Unterschied zwischen interner und externer Vektorquelle, aus Sicht des Cores.
Hallo A.K.! Wird diese Funktion aufgerufen werden für die Dauer der Abarbeitung alle IRQs gesperrt. void watchdog(void) __swi(0) { WDFEED=0xAA; WDFEED=0x55; } Ich habe es getestet und es traten beim besten Willen keine Spurious-Interrupts mehr auf. Aber was, wenn man nur einen bestimmten IRQ sperren möchte? Z.B. die Serielle. Ich habe folgendes probiert: U1IER= 0x00; // Serieller IRQ aus. ... U1IER= 0x07; // Alle IRQ-S ein (außer Modem) Mit dieser Konstruktion kam es auch immer wieder vor, dass Spurious-Interrupts auftraten. Ist es möglich einen einzelnen IRQ zu sperren, ohne dass später ein Spurious auftritt oder muss man immer alle IRQs mittels SWI sperren? Tschüss, Martin
@Martin, genau so ist es, die spurious Interrupts entstehen hauptsächlich beim Sperren einer einzigen Interruptquelle. Es wird wohl so sein, daß erstmal das Interruptflag den Interrupt auslöst und danach dann nochmal das Interruptflag gelesen wird, um nun den Vector zu bestimmen. Ist nun inzwischen genau diese Quelle gesperrt worden, kann der Vector nicht mehr bestimmt werden. Jeder auch nur einigermaßen bewanderte Hardwareentwickler hätte nun einfach alle Interruptbits nach dem ersten Lesen gelatcht, bis ein Vector gesendet wurde. Das ist eigentlich absolutes Designgrundwissen, wenn ich ein Signal später nochmal benötige, latche ich es einfach. Peter
"hätte nun einfach alle Interruptbits nach dem ersten Lesen gelatcht" Wie stellst du dir das Zusammenspiel zwischen Core und VIC vor? Da liest niemand automatisch die Interrupt-Bits. Die werden im VIC sämtlich schlicht zusammen-geodert und als eine einzige IRQ-Leitung an den Core weitergereicht. Und der fragt nicht zurück, sondern führt nur seinen IRQ-Handler aus. Erst dort wird per Software der Vektor abgefragt (und damit die Bits). Wenn aus diesem Modell etwas werden soll, dann muss das IRQ-Request-Bit im VIC "sticky" sein, d.h. es wird mit einlaufendem IRQ sofort gesetzt und bleibt dies so lange, bis der Handler es explizit zurücksetzt. Im UART-Beispiel von Martin würde allerdings im vergleichbaren Fall der UART-Handler auch dann aufgerufen, wenn dieser Interrupt mittlerweile vom o.A. Code ausgeschaltet wurde. Und die spurious Interrupts sind folglich immer noch da, nur ein bischen anders verpackt.
Hallo Leute, war ein paar Tage nicht im Internet und sehe zu meinem Thread 22 Postings. Da tut sich ja einiges, werde mich da auch wieder entsprechend zu äußern. Gruß Dietmar
@Peter Dannegger: "Jeder auch nur einigermaßen bewanderte Hardwareentwickler hätte nun einfach alle Interruptbits nach dem ersten Lesen gelatcht, bis ein Vector gesendet wurde. Das ist eigentlich absolutes Designgrundwissen, wenn ich ein Signal später nochmal benötige, latche ich es einfach." ==> Beim 8051 Z.B., sind Interruptflags fest gesetzt, da ist das kein Problem mit der späteren Bearbeitung. Manche werden dann automatisch gelöscht, z.B. TF0, manche auch nicht, z.B. TF2 beim Timer 2 Overflow. Man hat da auch keine Architektur, in der Prozessorkern und Peripherie komplett voneinander getrennt sind, wie beim ARM. Nun, beim ARM bzw. LPC2000 gibt es kein Flag-Register, sondern nur eine Interruptmeldung an den ARM-Core mit dazu gehörendem Vektor (oder auch nicht: dann Default Interrupt oder Spurious!!!). Wie meinst du das? Muß ich hier das VICRawIntr Register noch hinzuziehen, über das es auch keine weitere Erläuterung im User Manual gibt? Muß ich hier den Vectored Interrupt Controller (PL190) von ARM genauer erforschen??? Zur allgemeinen Beruhigung: Habe sowohl direkt am Interruptvektor als auch in der Default Interrupt Routine Fehlerzähler installiert, welche aber nach vielen Millionen interner (Timer, 1 ms) und externer Interrupts (10000 / sec) und einigen Betriebsstunden auf 0 stehen geblieben sind. Also: keine Spurious Interrupts aufgetreten. Ist der VIC Controller doch schnell genug? Warum ist dann das Thema noch so brisant? Ist der LPC2000 doch besser als sein Ruf durch diese Spezialitäten? Wer möchte, bekommt da gerne auch ein paar Schnipsel Code, zur Einfügung in die Startup.s von Keil z.B., über den ich dann gerne auch ein Feedback oder Verbesserungsvorschläge hätte. Gruß Dietmar
@Peter Dannegger: Beim LPC2000 kann man die Interruptfreigaben auf 2 Arten beeinflussen: Einmal direkt im ARM-Kern, durch Manipulation des I- und F- Bits im CPSR, oder über die VIC Register VICIntEnable bzw. VICIntEnClear. Gruß Dietmar
@A.K., und Martin: Der VIC Default Interrupt darf in keinem Fall acknowledged werden, z.B. mit VICVectAddr = 0 o.ä., sonst Endlosschleife im Default Interrupt! Der muß leer bleiben. Gruß Dietmar
@Martin: Ein einzelner FIQ hat eine eigene Sprungadresse und ist nicht vom VIC abhängig. Habe in meiner Anwendung 1 FIQ (Timer, zur absolut präzisen Zeitmessung) und etwa 10 IRQ. Den Watchdog reloade ich nur über eine SWI-Exception, nachdem ich feststelle, daß der Timer mit FIQ-Interrupt noch einige Zeit entfernt ist. SWI deshalb, damit ihn nicht IRQ durchbrechen. Dort sperre ich den FIQ (nur vorsichtshalber), und gebe ihn nach der Watchdog Feed Sequenz wieder frei. Gruß Dietmar
"Der VIC Default Interrupt darf in keinem Fall acknowledged werden, z.B. mit VICVectAddr = 0 o.ä., sonst Endlosschleife im Default Interrupt!" Interessant. Widerspricht direkt der AN10414, denn dort steht genau das drin. Die Beschreibung der VIC-Makrozelle macht eine solche Schreiboperation zwar nicht zwingend, da es unterhalb des Default Interrupts nix mehr gibt, aber stören sollte es eigentlich auch nicht.
"Ist der LPC2000 doch besser als sein Ruf" Wie ich oben schon sinngemäss schrieb. Ein Hersteller steht bei potentiellen Problemen, kleinen oder grossen, immer in dem Dilemma (a) sag ich nix und hoffe dass es keiner merkt weil selten, (b) ich dokumentiere es und ein paar Kunden flüchten davon weil ihnen die Fehler-Doku nun zu dick wurde. Heutzutage ist dank schlechter Erfahrungen mit (a) nun (b) üblich, aber das war nicht immer so. Zudem muss das Problem nicht zwingend auftreten. Es gibt diese Biester typischerweilse, wenn Interrupt-Ursachen wie von alleine wieder verschwinden. In AN10414 wird das als Resultat der Kopplung zweiter Interrupt-Ereignisse in der UART beschrieben. Wenn solche Geistereffekte nicht auftreten, gibt's auch keine sprurious Interrupts. Probiert das doch mal aus, indem du im Hauptprogramm sehr häufig das Interrupt-Flag des 1msec Timer zurücksetzt. Mit etwas Glück passiert das irgendwann einmal genau zu dem Zeitpunkt, zu dem der Core sich für den IRQ entschieden hat.
Worin liegt eigentlich der Grund für diese pingelige Handhabung vom Watchdog? Anders als bei AVR ist beim LPC2000 Feed ja keine Zeitbedingung drin. Es muss nur sichergestellt sein, dass der zweite Feed vor jedem anderen Watchdog-Zugriff drankommt. Wenn also beispielsweise die Handhabung vom Watchdog an einer einzigen Stelle der Mainloop erfolgt, kann man sich m.E. den ganzen Zirkus sparen. Ob dann in die Feed-Sequenz ein Interrupt jedweder Art reinrutscht, interessiert nicht wirklich.
Ich hab auch schon fleissig mitgelesen, aber mir ist eins noch nicht so ganz klar. Und zwar hab ich in meinem Cstartup.S folgendes: .global AT91F_Spurious_handler .func AT91F_Spurious_handler AT91F_Spurious_handler: b AT91F_Spurious_handler .size AT91F_Spurious_handler, . - AT91F_Spurious_handler .endfunc Was hat es für einen Sinn, dass der nur im Kreis springt und da nie wieder rauskommt?
Sowas macht man, wenn man annimmt, dass der Fall nicht eintrifft, und an dieser Stelle keine Idee für eine Debug-Ausgabe hat. Das Ergebnis ist ein harter Stop und der JTAG-Debugger zeigt an warum. Ob dieses Beispiel im Lichte von spurious Interrupts allerdings wirklich sinnvoll ist, möchte ich mal bezweifeln.
@A.K.: "Interessant. Widerspricht direkt der AN10414, denn dort steht genau das drin." Gibt es hier noch was spezielles zu beachten und ich darf den Default Interrupt nicht auch noch für nicht vektorisierte Interrupts verwenden? Erscheint mir ja auch merkwürdig: Hatte nicht vektorisierte Interrupts getestet, die natürlich auch in der Default-Routine auflaufen. Bei Bestätigung mit VICVectAddr = 0 hängt der Interrupt dann tatsächlich in einer Endlosschleife, womit man die gesamte Anwendung dann vergessen kann. Nun, ist ja auch klar, es ist ein nicht vektorisierter Interrupt, und der soll eben nicht bestätigt werden. Gruß Dietmar
@A.K.: Habe aus dem User Manual die Info, daß die Watchdog Feed Sequenz unmittelbar aufeinander folgen soll. Nun, einerseits geht das ja nicht, weil einige mehrere Assemblerbefehle dahinterstecken, um auf die Peripheral Register zuzugreifen. War mir bisher nicht so ganz klar. Liebend gerne schaffe ich eine aufwendigere Konstruktion wie den SWI dafür auch gerne wieder ab. Gruß Dietmar
@Thomas Pototschnig: Das ist dann wie bei den Exception-Vektoren? Wenn man sie nicht selbst ändert, hängen sie and er Stelle, wo der Vektor steht, fest (z.B. Startup.s von Keil). Also, da muß man selbst ran und was ändern. Gruß Dietmar
"Habe aus dem User Manual die Info, daß die Watchdog Feed Sequenz unmittelbar aufeinander folgen soll." Wo? Die einzige ähnliche Aussage, die ich finden kann, stellt nur fest, dass dazwischen kein anderer Zugriff auf den watchdog register space erfolgen darf. "Endlosschleife" Arbeitest du mit IRQ-Nesting? Wenn ja, solltest du die IRQs vor dem Acknowledge wieder ausschalten (so wird's auch in der entsprechenden AN gemacht). Andernfalls ergibt dieses Verhalten für mich keinen Sinn.
"ich darf den Default Interrupt nicht auch noch für nicht vektorisierte Interrupts verwenden?" Warum nicht?
@A.K.: Du scheinst doch schon etwas detaillierte Kenntnisse über ARM-Prozessoren zu haben, über die üblichen Anwendungen mit ARM-Mikrocontrollern hinaus. Erst mal Danke für deine Anregungen. Nun, als Entwickler ist man üblicherweise sonst auf sich alleine gestellt und auf den gesunden Menschenverstand angewiesen. Über die AN10414 gab es kürzlich eine Menge Aufruhr und Unstimmigkeiten im LPC2000-Forum von YAHOO (dieses Forum ist übrigens auch sehr empfehlenswert, jedoch "nur" englischsprachig). Da wäre z.B. ein Konflikt mit der Spurious Behandlung und der Betriebsart "Nested Interrupts per Software" (das ist eine Unterbrechung von IRQ durch andere IRQ durch Software-Trick). Das geht aber schon sehr ins Detail, und die Nested Interrupts braucht man nicht in jeder Anwendung, da ja klare Prioritätslogik. @Alle: Hatte ein Stück Behandlungscode zum Spurious Interrupt IRQ angekündigt, zur direkten Anbindung an den IRQ-Vektor, und poste das wegen der geringen Größe direkt hier im Textfenster. Der Code wird direkt vom IRQ-Vektor angesprungen und befindet sich in der Startup.s von Keil bei mir auch direkt hinter den Vektoren: IF (SPUR_IRQ_SETUP != 0) IRQ_Spurious_Handler: ; Sprung vom IRQ-Vektor 0x00000018: SUB LR, LR, #0x04 ; Justierung der Returnadresse STMFD SP!, {R0-R1, LR} ; sichere Register auf Stack MRS R0, SPSR ; Ist ein IRQ-Interrupt aufgetreten, TST R0, #I_Bit ; während IRQ-Interrupts gesperrt sind? LDMNEFD SP!, {R0-R1, PC}^ ; spurious Interrupt: kehre zurück ; berechne Sprung zum Interrupt ohne Zerstörung von Registern: ADD LR, LR, #0x04 ; Justierung der Returnadresse LDR R0, =VIC_VECT_ADDR ; echter IRQ-Interrupt: LDR R0, [R0, #0] ; lade Vektor von VicVectAddr MOV R1, SP ; lade aktuelle Stapeladresse ADD R1, R1, #0x08 ; Rücksprungadresse im Stapel STR R0, [R1] ; speichere Sprungadresse LDMFD SP!, {R0-R1, PC} ; Sprung zur Interuptbearbeitung ENDIF Die ersten 5 Zeilen entsprechen in etwa dem Vorschlag aus dem Philips User Manual. Was mir daran selbst nicht gefällt, ist, daß ich die Rücksprungadresse zum Interrupt direkt auf dem Stack manipulieren mußte. Eine andere Lösung habe ich programmiertechnisch noch nicht, da ich kein Register beschädigen wollte. Tritt ein (Spurious) Interrupt in dem Fall auf, wenn das Programm gerade versucht hat, das I-Bit zu disablen (I=1), erfolgt eine Rückkehr an der Stelle: LDMNEFD SP!, {R0-R1, PC}^ Natürlich entsteht hier für jeden IRQ eine erhöhte Reaktionszeit! Das stört jedoch nicht wirklich, weil hardwareabhängige Dinge wie z.B. Timer Capture, dann schon abgeschlossen sind und davon relativ unabhängig funktionieren. Im Simulator klappt das wunderbar. Was in der realen Anwendung passiert, habe ich noch nicht ganz genau heraus. Vielleicht werde ich ein reines Assembler-Testprogramm für den Simulator erstellen, auch um herauszufinden, was genau mit der Pipeline passiert. Das Debug-Fenster bei Keil ist da etwas unübersichtlich. Wer den Beispielcode verwenden möchte: Gerne, auf eigene Gefahr. Wenn jemand hier noch was sieht, was nicht ganz sauber und nicht in Ordnung ist, dann bin ich für jeden Hinweis dankbar. Gruß Dietmar
@A.K.: "ich darf den Default Interrupt nicht auch noch für nicht vektorisierte Interrupts verwenden? Warum nicht?" Also, dann laufen im Default Interrupt spuriouse und legale nicht vektorisierte Interrupts auf. OK, denkbar. Das ist z,B. mit einem Switch-Case über das Register VICIRQStatus machbar. Eine Unterscheidung, welche Interrupts ich acknowledgen muß, erübrigt sich auch, nämlich keine. Gruß Dietmar
@A.K.: "Endlosschleife" Nein, kein Interrupt Nesting, das war ein ganz simples Testprogramm mit einer minimalen Main-Schleife und einem einzigen nicht vektorisierten Interrupt. In anderen Exceptions wie PAbt, DABt, Undef, darf man auch kein Acknowledge verwenden, weil das gleiche Prinzip: Die nicht vektorisierten Interrupts werden doch nicht vom VIC-Controller behandelt! Gruß Dietmar
"In anderen Exceptions wie PAbt, DABt, Undef, darf man auch kein Acknowledge verwenden" Logisch. Die haben ja auch rein garnichts mit dem VIC zu tun. Die (dokumentierte) Logik beim VIC ist jedoch, dass zu einem Lesevorgang auf VicVectAddr immer auch ein abschliessender Schreibvorgang gehört. Wenn allerdings in einem sehr einfachen Interruptsystem der ARM IRQ-Handler den Vektor garnicht abfragt, sondern der Code an 0x18 statt dessen direkt den IRQ-Handler anspringt, dann ergibt auch das Acknowledge keinen Sinn.
TST R0, #I_Bit ; während IRQ-Interrupts gesperrt sind? LDMNEFD SP!, {R0-R1, PC}^ ; spurious Interrupt: kehre zurück" Oha, jetzt kommen wir der Sache näher! Ich hatte oben erwähnt, dass es rund um ARM-Interrupts zwei Themen gibt, die gerne miteinander verwechselt werden. Auch in der Yahoo group. Auch die Philips AN trennt das nicht deutlich genug. Das was du hier abhandelst, ist garnicht der "spurious interrupt". Sondern dass, was in http://water.cse.unsw.edu.au/esdk/lpc2/spurious-irq.html als "surprise interrupt" bezeichnet wird, also das Problem bei gleichzeitigen Abschalten von IRQ und FIQ per CPSR. Was mit dem VIC wiederum nichts zu tun hat.
Um das mal kurz und "deutsch" zusammenzufassen: (1) "surprise interrupt": IRQ und FIQ werden gleichzeitig per MSR-Befehl abgeschaltet. Aufgrund einer Eigenheit der ARM-Cores kann dennoch der IRQ-Handler aktiv werden, dann aber mit abgeschaltetem FIQ was massiv dessen Latenzzeit schadet. Hierzu siehe Philips AN10254 und FAQ Eintrag auf www.arm.com. (2) "spurious interrupt": Der VIC reicht eine Interrupt-Anforderung als IRQ durch, findet aber zum Zeitpunkt der Vektorerzeugung (das ist der Ladebefehl auf VicVectAddr) keine Interrupt-Anforderung mehr vor, weil die in der Zwischenzeit wieder verschwand. Da der VIC die Interrupt-Anforderung nicht speichert, kann er keinen korrekten Vektor erzeugen und liefert den Standardvektor (Atmel SAM7 hat dazu einen eigenen Vektor). Beide Fälle müssen getrennt betrachtet werden. Für (1) tut es z.B. der von dir gezeigte Code. Natürlich muss da auch nichts in VicVectAddr geschrieben werden, denn es wurde ja auch nichts gelesen. Man kann sich den IRQ-Handler auch vereinfachen, indem man, wie in der AN gezeigt, den FIQ nie in einem Befehl zusammen mit dem IRQ abschaltet. Bei (2) gehen die Meinungen etwas aueinander. Philips legt nahe, im Default-Handler alle möglichen Ursachen von spurious Interrupts abzufragen. Diesen ausgesprochen komplexen Weg kann ich jedoch nicht ganz nachvollziehen (der Autor im o.A. Link wohl auch nicht). Bei (2) wird dein Code den Vektor aus VicVectAddr lesen und in den Default-IRQ-Handler springen. Wenn nested Interrupts nicht verwendet werden, reicht es m.E. aus, dort die ggf. fälligen nichtvektorierten IRQs abzuhandeln, anschliessend per Schreibzugriff auf VicVectAddr den VIC wieder freizugeben und dann Return. Diese Vorgehensweise wurde auch in Beträgen in der Yahoo-Group skizziert
@A.K.: Na klar, ich möchte beiden Problemen vorbeugen, da ich auch mal das I-Bit und das F-Bit verändern möchte. Die FAQ auf der ARM Homepage ist eine Teilmenge der Beschreibung im Philips User Manual. Philips räumt noch die Sache mit dem VIC mit ein, ARM nicht. Spurious Interrupts des VIC konnte ich bisher nicht einen einzigen registrieren. Und, laß den doch alle 10 Minuten mal kommen, damit kann ich wahrscheinlich noch leben. Warum den Default Handler ausbauen? Wenn nicht acknowledged, kommt der Interrupt doch nochmal, und zwar mit Vektor, oder? Gruß Dietmar
"Die nicht vektorisierten Interrupts werden doch nicht vom VIC-Controller behandelt!" Selbstverständlich werden auch nichtvektorierte Interrupts vom VIC behandelt. Immerhin reicht er sie durch, wie auch die FIQs. Und liefert bei der fälligen Abfrage von VicVectAddr den Default-Vektor.
"Na klar, ich möchte beiden Problemen vorbeugen, da ich auch mal das I-Bit und das F-Bit verändern möchte." Darfst du ja auch, Du darfst auch beide abschalten wie es dir beliebt. Wenn du es allerdings einrichten kannst, nie beide zusammen mit dem selben MSR-Befehl abzuschalten, sondern allenfalls nacheinander, dann kann die Sonderbehandlung im IRQ-Handler entfallen. "Wenn nicht acknowledged, kommt der Interrupt doch nochmal, und zwar mit Vektor, oder?" Wenn der ursprüngliche Interrupt-Auslöser es sich anders überlegt und die Anforderung gelöscht hat, dann kommt dieser zweite nicht und die Anforderung geht verloren. Mir fällt bloss kein Szenario ein, wo mich dieses Verhalten stört. Im Fall der UART mit CTI und RDA beschreibt Philips recht deutlich, wie "spurious interrupts" auftreten können und dass sie schwer zu vermeiden sind. Soweit d'accord. Es wird freilich nicht erklärt, weshalb im default handler unbedingt die UART gelesen werden muss. Es kann ja eigentlich nichts schlimmeres passieren, als dass der fällige Interrupt ein paar Bytes später erneut anlandet, nämlich sobald der CTI Timer wieder zuschlägt oder das FIFO hinreichend voll ist. Allerdings wird "Jayasooriah" m.E. zum Prinzipienreiter, wenn er nahelegt, dass jeder "spurious interrupt" einer zuviel und das System beim Auftreten eines solchen unbrauchbar ist, bloss weil er die Aussage in der VIC-Dokumentation nicht verstanden hat. "Philips räumt noch die Sache mit dem VIC mit ein, ARM nicht." Für ARM sind Core und VIC zwei paar Stiefel. Der "spurious interrupt" wird, etwas verschlüsselt, in der Doku des VIC erwähnt.
Hallo Leute! Ich weiß, dass das jetzt nicht ganz zum Thema gehört, aber ich habe eine grundsätzliche Frage zu Nesting-IRQs. Es gibt ja von Keil so nette Makros bzw. in der Doku AN10381 sind diese auch beschrieben, wie man es schafft, die IRQs in einer IRQ-Routine wieder freizugeben. Wenn jetzt z.B. die Nesting-IRQs bei der UART angewendet werden. Sagen wir die UART empfängt ein Zeichen, die UART-IRQ-Routine wird angesprungen, die IRQS werden nach dem Eintreten wieder freigegeben. Nun befindet sich der Programm-Counter in der der IRQ-Routine und nun tritt ein nächster IRQ auf, der besagt, dass der Sende-Buffer der UART leer ist. Wird nun dieselbe IRQ-Routine nocheinmal angesprungen oder gilt dies nur für den Fall, dass ein anderer IRQ auftritt? Kann sich die Routine durch einen nächsten IRQ selbst unterbrechen oder nicht? Danke. Gruß, Martin
Theoretisch sollte die Prioritätslogik des VIC verhindern, dass ein Interrupt von einem gleicher Priorität unterbrochen wird. Praktisch würde ich darauf keine Wetten abschliessen, da das ganze Prioritätsgedöns vom VIC m.E. nicht schlüssig ist. Siehe http://www.mikrocontroller.net/forum/read-1-343285.html#343285
Hallo Leute! Wie ich das sehe, lasse ich das für den Anfang mit den Nestet-IRQs. Aber mit den Spurious-IRQs arbeite ich noch einbisschen. Ich habe mir das Beispiel mit dem Timer aus der Doku AN10414 Spurious-Interrupts noch etwas angeschaut. VICIntEnClr=0x... // Disabling interrupts in the VIC WDFEED=0xAA; // Feeding the Watchdog WDFEED=0x55; VICIntEnable=0x.. // Enabling interrupts in the VIC Das Lustige daran war, dass wenn ich z.B. folgende Zeilen zusätzlich einbaute, dass der Spurious-IRQ beim besten Willen nicht ausgelöst wurde. VICIntEnClr=0x... // Disabling interrupts in the VIC IOSET1=0x01<<16; WDFEED=0xAA; // Feeding the Watchdog WDFEED=0x55; IOCLR1=0x01<<16; VICIntEnable=0x.. // Enabling interrupts in the VIC Sehr sonderbar. Hat jemand eine Erklärung für dieses Phänomen? Gruß, Martin
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.