Hallo,
man kann ja eine ISR erstellen, welche die Standard-ISR
"__bad_interrupt" ersetzt. Das kann dann dafür benutzt werden, um einen
ausgelösten Interrupt, welcher aber keine ISR besitzt, abzufangen.
Ich frage mich, ob es eine Möglichkeit gibt innerhalb dieser
__bad_interrupt ISR herauszufinden, welcher Interrupt ursprünglich
ausgelöst wurde?
Mein Gedanke war zuerst, dass man die Standard-Vektor-Tabelle ersetzt
und hier statt einem jmp auf __bad_interrupt ein call benutzt. Dann
könnte man sich den dazugehörigen Vektor vom Stack holen. Man muss eben
nur aufpassen, dass der Wert vor dem reti vom Stack wieder runter ist.
Das wäre dann aber mit einem größeren Aufwand verbunden. Kennt jemand
eine bessere Lösung?
Bis dann,
netb
Noch eine kleine Verbesserung: Ich meinte nicht die aktuelle Version vom
AVRGCC sondern vielmehr die aktuelle Version vom WINAVR, falls es jemand
so genau wissen will :)
netb schrieb:> Ich frage mich, ob es eine Möglichkeit gibt innerhalb dieser> __bad_interrupt ISR herauszufinden, welcher Interrupt ursprünglich> ausgelöst wurde?
So etwas fragt man am besten das Datenblatt.
Oliver
Also der gcc wird dir den vector jmp nicht durch einen call ersetzen.
Was man aber machen kann ist im Interrupt die Flags abfragen, bei
einigen Interruptquellen bleiben diese eben erhalten.
Aber das ist eigentlich extremer Pfusch, wenn die Interrupts eingestellt
sind, sollte auch eine entsprechende ISR da sein.
netb schrieb:> Mein Gedanke war zuerst, dass man die Standard-Vektor-Tabelle ersetzt> und hier statt einem jmp auf __bad_interrupt ein call benutzt.
Müsste gehen, ich wüsste halt nur nicht, wie man das gcrt1.S selbst
so umschreiben kann, dass die Vektortabelle in dieser Form generiert
wird. Die "richtig belegten" Interruptvektoren müssen ja nach wie
vor über JMP/RJMP erreicht werden, da andernfalls nach der ISR der
Programmfluss beim nächsten Interruptvektor fortgesetzt würde oder
aber jede ISR selbst zuerst die Adresse aus dem Bereich der
Vektortabelle entfernen müsste.
Wenn man natürlich genau weiß, welche Vektoren man wirklich belegt
hat und welche nicht, kann man eine passend zurecht geschnittene
Tabelle implementieren.
Floh schrieb:> Aber das ist eigentlich extremer Pfusch, wenn die Interrupts eingestellt> sind, sollte auch eine entsprechende ISR da sein.
Beim Debuggen kommt es trotzdem schon hin und wieder mal vor, dass
man gar nicht weiß, warum man jetzt eigentlich dahin geschmissen
wird, weil vielleicht das Aktivieren des entsprechenden Interrupts
nicht bewusst vorgenommen worden ist oder gar durch einen "wild
laufenden Zeiger" oder sowas erfolgt ist. Dann hilft es in der ISR
für __bad_interrupt schon ein wenig, wenn man erst einmal weiß,
welcher Interrupt denn zugeschlagen hat, denn dann kann man
rückwärts aufdröseln, warum das entsprechende Bit gesetzt worden
ist (ggf. über einen "data breakpoint" im Debugger). BTDT.
Ich habe mir übrigens in diesem Falle einfach mittels eines Scripts
ISR-Stubs schreiben lassen für jede ISR, die auf __bad_interrupt
rauskam. Jede dieser ISRs hat eine Zahl in eine globale Variable
eingetragen und ist danach auf eine zentrale Stelle gesprungen, auf
die man im Debugger einen Breakpoint setzen konnte.
Hallo,
danke erst einmal für eure Ideen, dank dieser Ansätze bin ich nun schon
ein Stück weiter.
@Floh: Das hatte ich mir auch schon überlegt, doch das endet bei einem
großen AVR halt sehr schnell in tausenden von if-Abfragen.
@Jörg: Der Hinweis mit der gcrt1.S hat mir einen neuen "Lösungsansatz"
gegeben. Ich habe mir die Datei mal genauer angesehen. Da alle
Vektor-Symbole das "weak" Attribut besitzen, wird die Sprungadresse,
welche als Initialisierung den Wert "__bad_interrupt" besitzt, mit der
richtigen Adresse einer ISR überschrieben, wenn eine solche ISR
definiert wurde.
Zu dem Zeitpunkt, an dem der Assembler die gcrt1.S jedoch "bearbeitet"
besitzt das jeweilige Vektor-Symbol noch den Initialisierungswert und
daher kann im Macro keine Unterscheidung getroffen werden, ob es sich um
einen tatsächlichen __bad_interrupt handelt oder nur um ein
Vektor-Symbol, welches später noch überschrieben wird. Damit kann man
entweder allen Vektor-Symbolen ein Call geben oder gar keinem.
Allerdings bin ich auf eine andere Idee gekommen: Wenn ich keine
Informationen aus der Adresse ziehen kann, von der aus gesprungen wird
(weil ich kein Call benutze), so kann ich jedoch Informationen aus der
Adresse ziehen, zu der gesprungen wird.
Anstatt jedes Vektor-Symbol also stur mit __bad_interrupt zu
initialisieren, kann man ja auch einen Offeset hinzurechnen.
Also der Vektor 1 springt dann auf __bad_interrupt + 0, Vektor 2 auf
__bad_interrupt + 8 u.s.w. Das Makro lässt sich ja dahingehend leicht
modifizieren.
Die Funktion __bad_interrupt wird dann mit Hilfe eines Makros
folgendermaßen aufgebaut:
1
00000316 <__bad_interruptx>:
2
316: 8f 93 push r24
3
318: 80 e0 ldi r24, 0x00 ; 0
4
31a: 0c 94 db 01 jmp 0x3b6 ; 0x3b6 <__vectors_pop>
5
6
0000031e <__vector_2>:
7
31e: 8f 93 push r24
8
320: 81 e0 ldi r24, 0x01 ; 1
9
322: 0c 94 db 01 jmp 0x3b6 ; 0x3b6 <__vectors_pop>
u.s.w.
Also Vektor 1 springt hier auf Adresse 0x316, wo das Register 24
gerettet und anschließend ein Interrupt-Code dort rein geschrieben wird.
Danach springt er weiter zu __vectors_pop.
In __vectors_pop wird das Register r24 wieder vom Stack geholt und
danach springt er zur Reset-Adresse.
Wenn man nun eine ISR mit den Vektor "BADISR_vect" anlegt, springt er
anstatt auf __vectors_pop zu dieser ISR. Diese sollte dann "naked" sein
und man findet hier im Register r24 den Interrupt, der den Sprung
verursacht hat. Bevor man nun die ISR mit reti verlässt, muss man nur
noch das Register r24 manuell vom Stack holen.
r24 habe ich hier einfach aus dem Bauch heraus gewählt, es kann
natürlich jedes beliebige andere Register sein.
Ok, das ist jetzt allerdings doch recht kompliziert und es verursacht
einiges an Code-Overhead. Zudem wäre es mir am liebsten, dass man eine
solch veränderte Interrupt-Tabelle in ein Projekt integriert, ohne dass
man etwas an der WinAVR oder AVR-LibC Installation ändern muss.
Allerdings legt die AVR-LibC die Tabelle ja automatisch an, was man erst
einmal verhindern muss.
Ich bin also noch nicht ganz zufrieden damit, vielleicht fällt mir oder
euch noch was besseres ein.
netb schrieb:> Also Vektor 1 springt hier auf Adresse 0x316, wo das Register 24> gerettet und anschließend ein Interrupt-Code dort rein geschrieben wird.> Danach springt er weiter zu __vectors_pop.>> In __vectors_pop wird das Register r24 wieder vom Stack geholt und> danach springt er zur Reset-Adresse.
1) Wenn ein Reset das Ziel ist, brauch R24 weder gesichert noch
restauriert zu werden.
2) Wenn du eh zu RESET springst, wozu das alles? Das macht
__bad_interrupt eh schon.
3) Zum RESET-Vektor zu springen ist keine gute Idee. Die Hardware wird
dadurch nicht neu initialisiert. Besser: WDT aufziehen und warten bis
der einen RESET auslöst. Danach steht dann auch die Hardware wieder auf
"neu": Timer, UART, ...
4) R24 kannst du in einer statischen noinit-Variablen sichern. In MCUSR
erkennst du die RESET-Ursache. Bei WDT liest du die Variable aus und
kannst sie verfügbar machen (UART, Display, ...) und damit den Vektor
rausfinden.
Hallo Johann,
natürlich hast du recht. Wenn man nur zum Resetvektor springt ist das
ganze alles egal. Das Wiederherstellen von r24 habe ich eher der
Vollständigkeit halber hier integriert.
Das Ziel ist ja eher eine eigene ISR mit dem BADISR_vect zu erstellen
und in dieser mit Hilfe der Information "welcher Interrupt war für den
Sprung verantwortlich" etwas anzufangen.
Beispielsweise eine Debug-Ausgabe, oder das Heraussuchen einer eventuell
gespeicherten Sprungadresse im SRAM.
Bis dann,
netb