Forum: Compiler & IDEs __bad_interrupt und der dazugehörige Interrupt-Vektor


von netb (Gast)


Lesenswert?

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

von nix_könner (Gast)


Lesenswert?

STM32? oder welcher einer?

von netb (Gast)


Lesenswert?

Hallo,

ah ja, Entschuldigung. Ich arbeite hier mit einem AtXMega128A1 und mit 
der aktuellen Version vom AVRGCC und der AVR-LibC.

Bis dann,
netb

von netb (Gast)


Lesenswert?

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 :)

von Oliver (Gast)


Lesenswert?

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

von Floh (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von netb (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von netb (Gast)


Lesenswert?

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

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
Noch kein Account? Hier anmelden.