Forum: Compiler & IDEs Interruptvektortabelle


von Tobi (Gast)


Lesenswert?

Hi!

Ich arbeite auf einem ATMEGA32 mit Bootloader. Die Interrupts sollen im
Bootloader verarbeitet werden (das funktioniert soweit).
Vom Bootloader getrennt soll es nun eine Applikation geben. Da aber die
Interruptvektoren im Bootloader stehen, müssen diese auf die
"Applikations-Tabelle" umgebogen werden, damit Interrupts auch in der
Applikation abgearbeitet werden können.
Also ergeben sich 2 Fragen:

1. Wie kann man die Interrupttabelle des Bootloaders ändern? Der RESET,
UART_RECV und TIMER0_OVFL Interrupt sollen aber unverädert bleiben.
Wenn ich versuche an der Stelle, wo die entsprechenden
Interruptvektoren liegen (bei mir 0x7000-0x7054, 4k Bootsektor) eine
Section anzulegen, meckert der Linker (Ist ja auch klar, da eine
Adressüberschneidung vorliegt)
Kann man nicht dem Linker die ISR-Func Adressen irgendwie manuell
mitgeben?

2. Da vom Bootloader aus dann die Adresse 0x0000 angesprungen werden
soll, darf das Startup in der Applikation nicht mehr ausgeführt werden!
(Würde beim Rücksprung in den Bootloader alles schmeißen, da der SP,
usw. falsch wären)
Wie kann man verhindern, dass ein Startup-Code miteingebunden wird?

Vielen Dank schon mal!
MfG Tobi

von Tobi (Gast)


Lesenswert?

Nachtrag:
Hab vermutet, dass das mit der "gcrt1.S" (Startupcode) zusammnhängt,
kann die Datei weder auf meinem Rechner (WIN-AVR 4.0 + GCC) noch im
Netz fiden!!!

von Jörg Wunsch (Gast)


Lesenswert?

> Hab vermutet, dass das mit der "gcrt1.S" (Startupcode)
> zusammnhängt, kann die Datei weder auf meinem Rechner (WIN-AVR
> 4.0 + GCC) noch im Netz fiden!

(Deine Ausrufezeichentaste klemmt.  Eigentlich musst du das auch gar
nicht ausrufen, wen willst du denn hier anschreien? ;-)

So?  Wo hast du denn da gesucht?  Wenn du einfach nur den Sourcecode
mit installiert hättest, hättest du diese Datei auf jeden Fall
dabei gehabt.

Was soll eigentlich "WIN-AVR 4.0 + GCC" sein?  WinAVR enthält den
GCC, compiliert für den AVR (auch bekannt als AVR-GCC), aber was
dabei die Versionsnummer 4.0 soll, weiß ich nicht.  WinAVR benennt
seine Versionen nach Datum.  Vom GCC gibt es zwar eine Version 4.0,
aber die gilt besonders für das Target AVR noch als recht
,grün', sodass sie wohl selbst in der nächsten WinAVR-Version
noch nicht dabei sein wird.

> 1. Wie kann man die Interrupttabelle des Bootloaders ändern? Der
RESET,
> UART_RECV und TIMER0_OVFL Interrupt sollen aber unverädert bleiben.
> Wenn ich versuche an der Stelle, wo die entsprechenden
> Interruptvektoren liegen (bei mir 0x7000-0x7054, 4k Bootsektor) eine
> Section anzulegen, meckert der Linker (Ist ja auch klar, da eine
> Adressüberschneidung vorliegt)

Wenn du sowas exotisches machen willst, dann solltest du dich zuvor
wohl mit der Art und Weise, wie die Interruptvektoren in der avr-libc
verwaltet werden, tiefgreifend auseinandersetzen.

Vorab: ich denke, dass es die einfachste und sinnvollste Lösung
wäre, im Bootloader einfach auf die Interrupts zu verzichten und
dann die Vektortabelle in der Applikation zu belassen.  Aber das ist
deine Sache, wenn du den komplizierteren Weg gehen willst, musst du
dir wohl oder übel die Mühe machen, den Krempel auch
tiefgreifend erstmal zu verstehen.

Bis zu gcrt1.S bist du ja zumindest schon vorgedrungen.  Dass du aber
nicht in der Lage warst, diese Datei sinnvoll zu finden, lässt mich
ein wenig zweifeln, ob du die restlichen nötigen Details auch
wirklich beherrschen werden könnst.  Anyway, gcrt1.S definiert
einfach in einer Schleife alle Interruptvektoren so, dass die auf
__bad_interrupt zeigen.  Alle diese Eintrittspunkte sind als `weak'
deklariert, damit wird entsprechenden gleichnamigen Eintrittspunkten,
die von der Applikation geliefert werden, dann vom Linker der Vorrang
eingeräumt.  (Ohne `weak' würde sich der Linker über die
Doppeldefinition beklagen.)  Die Vektoren sind übrigens nur einfach
__vector_1 bis __vector_N durchnummeriert (das hättest du schon aus
dem Aufbau des SIGNAL-Makros erfahren können).

Du musst also letztlich in deinem Bootloader alle (dich
interessierenden) Eintrittspunkte __vector_1 bis __vector_N belegen.

> Kann man nicht dem Linker die ISR-Func Adressen irgendwie manuell
> mitgeben?

Im Prinzip sollte sogar das gehen, indem du auf der Linker-
Kommandozeile entsprechende Symboldefinitionen übergibst.  Ist
natürlich je nach Prozessor einiges an Schreibarbeit, aber wenn du
das unbedingt willst...  Beispiele solltest du in der avr-libc-Doku
(indirekt) finden, dort beispielsweise in der Erläuterung, wie man
die Speicherallozierung von malloc() auf andere Bereiche (bspw. den
externen SRAM) umbiegen kann.

> 2. Da vom Bootloader aus dann die Adresse 0x0000 angesprungen werden
> soll, darf das Startup in der Applikation nicht mehr ausgeführt
> werden!  (Würde beim Rücksprung in den Bootloader alles
> schmeißen, da der SP, usw. falsch wären)

Das leuchtet mir nun überhaupt nicht ein.  Warum willst du denn
zwischen Applikation und Bootloader hin- und herspringen?  So ist das
eigentlich nicht gedacht.  Normalerweise fangen beide so an, als
hätten sie den kompletten Prozessor und wären die alleinigen
Programme auf dem Chip.  Wenn der Bootloader mit dem Laden fertig ist,
springt er einfach die Applikation an, und diese beginnt dann wie nach
einem Reset.  (Sie sollte sich lediglich dessen bewusst sein, dass die
IO-Register nicht ,jungfräulich' sind.)  Falls die Applikation den
Bootloader anspringen will, beginnt der dann ebenfalls wieder ganz von
vorn, d.h. die Applikation hat an dieser Stelle einfach die Steuerung
aufgegegen.

Es tut übrigens dabei nichts zur Sache, ob man ein wenig `shared
code' nehmen will, also z.B. Funktionen des Bootloaders aus der
Applikation aufrufen will.  In diesem Falle hat halt die Applikation
über den Stack die Oberhoheit, aber das ist ja überhaupt nicht
tragisch.  Lediglich bei Benutzung globaler Variablen muss man die
Ressourcenverteilung manuell organisieren, falls der shared code auf
solche zugreifen können soll.  Das sollte sich aber durch
Verschieben der .data-Section in einem von beiden Teilen machen
lassen.

> Wie kann man verhindern, dass ein Startup-Code miteingebunden wird?

Das wird wirklich nur gehen, indem du deinen eigenen Startup-Code
schreibst.  Ehrlich gesagt: die am wenigsten wünschenswerte
Variante.  Ich würde sowas versuchen zu vermeiden, da es eine
maintenance nightmare werden kann.

von Peter D. (peda)


Lesenswert?

"2. Da vom Bootloader aus dann die Adresse 0x0000 angesprungen werden
soll, darf das Startup in der Applikation nicht mehr ausgeführt
werden!
(Würde beim Rücksprung in den Bootloader alles schmeißen, da der SP,
usw. falsch wären)"


Soweit ich weiß, wird der Stack immer noch zweimal gesetzt, das 2. mal
im Main.
Das Main kann also weder rekursiv aufgerufen werden, noch irgendwohin
zurückkehren.

Da das kaum einen stört, wurde es bisher wohl nicht als Bug eingestuft.
Es sind quasi 8 Bytes überflüssiger Code.


Peter

von Chris (Gast)


Lesenswert?

btw, soweit ich weiß verbietet der C-Standard das Aufrufen der Funktion
main() vom Benutzer-Code.

von Jörg Wunsch (Gast)


Lesenswert?

An welcher Stelle verbietet er das?  Mir ist keine bekannt.

von Chris (Gast)


Lesenswert?

> An welcher Stelle verbietet er das?  Mir ist keine bekannt.

Mist, schon wieder C++ mit C verwechselt (im Anhang C.1.2 im
C++-Standard stehts); in C++ darf man nichtmal die Adresse von main
nehmen.

Anhang C listet die Inkompatibilitäten von C++ zu C auf. Das heißt du
hast Recht, in C durfte man main() wohl rekursiv aufrufen.

von Tobi (Gast)


Lesenswert?

Hi!

@ Jörg:
Vielen Dank für die ausführliche Antwort. Nein, anschreien will ich
keinen :-)
Meinte eigentlich das AVR-Studio 4.0 (zum debuggen) mit dem AVR-GCC.(Da
hab ich einen Wurm reingebracht.)

> Bis zu gcrt1.S bist du ja zumindest schon vorgedrungen.  Dass du
> aber nicht in der Lage warst, diese Datei sinnvoll zu finden, lässt >
mich ein wenig zweifeln, ob du die restlichen nötigen Details auch
> wirklich beherrschen werden könnst.

Ja, ich zweifle auch dran, dass ich das hinbekomme. Ehrlich gesagt, bin
ich recht neu IM AVR-Gewerbe. Ich kenn mich mit dem C505 recht gut aus
und hab da mit der KEIL µ-Vision 2 Entwicklungsumgebung gearbeitet. Da
hab ich sowas wie oben beschrieben bereits am Laufen :-) Da gibt's ja
die "Startup.asm", die man leicht einbinden kann und damit lassen
sich die Interruptvektoren umgebiegen und die restlichen
Initialisierungen selbst einfügen. Nur sowas hab ich im GCC direkt noch
nicht gefunden
Das die Vektoren von 1 .. N im SIGNAL.H definiert sind, hab ich schon
gewusst :-)

Meine Idee war einen voll ohne Applikation laufenden Bootloader
hinzubekommen. Dieser soll dann überprüfen, ob es eine gültige
Applikation (durch Berechnung einer CRC16 Checksumme über den
Application-Flash Bereich, die im EEPROM abgespeichert ist) gibt und
diese dann "anspringen". Falls es keine Applikation gibt, läuft der
Bootloader alleine. Das setzt vorraus, dass ich die Interrupts im
Bootloader halten muß. Der Bootloader soll im Falle einer ungültigen
Applikation über die UART dann die (nun gültige) Applikation empfangen
und brennen.
Man kann dann sich ganz leicht ein PC Programm (Denke mal, wenn ich
hier erwähne, dass ich unter Windows programmiere gibt's Ärger :-))
)zaubern, das per UART mit dem Bootloader des ATMEGA quatscht. So kann
ich Applikationen auf Controllerebene basteln und diese dann beqeuem
und schnell in den ATMEGA brennen.
Daher müssen die Interrupts (eigentlich ja nur der UART-Interrupt) im
Bootloader stehen.

von Jörg Wunsch (Gast)


Lesenswert?

> Ich kenn mich mit dem C505 recht gut aus und hab da mit der KEIL
> µ-Vision 2 Entwicklungsumgebung gearbeitet. Da hab ich sowas wie
> oben beschrieben bereits am Laufen :-) Da gibt's ja die
> "Startup.asm", die man leicht einbinden kann und damit lassen sich
> die Interruptvektoren umgebiegen und die restlichen
> Initialisierungen selbst einfügen.

Prinzipiell kannst du das mit GCC/avr-libc auch machen, aber ich halte
diese Vorgehensweise für grundlegend riskant.

Zwischen C run-time startup (crt) und Compiler besteht eine Art
,Vertrag', die internen Details sind dabei Verhandlungssache zwischen
diesen beiden und keine öffentliche Schnittstelle.  Dadurch werden
solche Dinge garantiert wie das Herstellen des normgerechten Zustands
bis zum Start von main().  Falls in Absprache zwischen Compiler und
Bibliothek daran mal aus irgendwelchen Gründen Änderungen vorgenommen
werden sollten, möchten die beiden das tun können, ohne noch mit
weiteren Partnern verhandeln zu müssen.  In einem solchen Falle
riskierst du nach einem Upgrade der Entwicklungsumgebung einfach, dass
du diesen Vertrag brichst (da du dein eigenes, dann nicht mehr
vertragsgemäßes startup benutzt).


> Nur sowas hab ich im GCC direkt noch
> nicht gefunden

Braucht man eigentlich auch nicht.  avr-libc hat sich die größte Mühe
gegeben, alle Arten von customization als Optionen vorzusehen (durch
Modifikation von globalen Linkersymbolen von der Kommandozeile aus
oder durch Benutzung der .initN-Sections).  Der einzige Mangel, der
mir am derzeitigen gcrt1.S bekannt ist ist, dass man die
Interruptvektoren nicht ausblenden kann (wäre für bootloader zum
Platzsparen interessant).  Das sollte sich aber auch noch einbauen
lassen.

> Das die Vektoren von 1 .. N im SIGNAL.H definiert sind, hab ich
schon
> gewusst :-)

signal.h (Kleinschreibung) ;-)  C ist case sensitive.

> Meine Idee war einen voll ohne Applikation laufenden Bootloader
> hinzubekommen. Dieser soll dann überprüfen, ob es eine gültige
> Applikation (durch Berechnung einer CRC16 Checksumme über den
> Application-Flash Bereich, die im EEPROM abgespeichert ist) gibt und
> diese dann "anspringen". Falls es keine Applikation gibt, läuft
der
> Bootloader alleine.

Das ist vernünftig.

> Das setzt vorraus, dass ich die Interrupts im
> Bootloader halten muß.

Überhaupt nicht.  Für das bisschen bootloader brauchst du keine
Interrupts.  Den reset-Vektor kannst du unabhängig von den restlichen
Vektoren verbiegen, und das sollte genügen.

Die UART-Routinen bekommst du auch ohne Interrupts schnell genug hin.
Fülle einfach für jede ROM-Page einen internen Puffer und definiere in
deinem Protokoll, dass nach jeder Page auf eine Bestätigung durch den
Bootloader gewartet werden muss.  Das erspart dir selbst irgendwelche
Notwendigkeit für ein handshake.

Beispiele für AVR910-style bootloader gibt's wie Sand am Meer.  Es
gibt auch ein oder zwei Beispiele für STK500-style loader, das ist
besonders dann interessant, wenn man über einen USB-seriell-Wandler
arbeiten will, da das STK500-Protokoll besser den Anforderungen des
paketorientierten Datentransfers via USB gerecht wird als das dumme
request-response-Protokoll von AVR910.  Das beste mir bekannte
Beispiel findest du bei Matthias Weißers USBisp, ein Bootloader, der
in die 2 KB boot section eines ATmega8 passt und ein abgerüstetes
STK500-Protokoll spricht.

Die Software vom USBisp steht unter GPL.  Wäre die Frage (die du
Matthias sicher auch einfach selbst stellen kannst), selbst wenn man
das Teil in einem kommerziellen Projekt benutzen will, sollte die GPL
meiner Meinung nach dann erfüllt sein, wenn man seine Änderungen am
Bootloader veröffentlicht, ohne dass die eigentliche Applikation davon
berührt ist.  Das wird in den meisten Fällen tolerabel sein,
alternativ kannst du ja versuchen, ob du für den bootloader mit
Matthias eine BSD-style license aushandeln kannst.
(,Veröffentlichen'
bedeutet in diesem Falle die Weitergabe deiner eigenen Änderungen im
Sourcecode an diejenigen, denen du dein Gerät gibst, wobei diese es
dann auch beliebig anders weitergeben oder veröffentlichen dürfen.  Du
musst den Sourcecode auch nur auf Anforderung hin weitergeben, musst
aber den Empfänger auf sein Recht hinweisen.)

von Peter Dannegger (Gast)


Lesenswert?

"und damit lassen sich die Interruptvektoren umgebiegen"

Es scheint mir, als ob Du das Datenblatt nicht gelesen hast.
Alle ATMega außer dem ATMega48 haben das IVSEL-Bit, womit bequem die
Interrupts in den Bootloader und zurück geschaltet werden können.

Den Startup-Code zu verbiegen, ist also nicht nötig.


Peter

von Tobi (Gast)


Lesenswert?

Nochmal Hi!

@ Peter:
Doch, das weiß ich. Würde bei meinem Konzept aber nicht viel helfen ...


@ Jörg
Aber jetzt muß ich mal mein Konzept überdenken, Danke!

von Tobi (Gast)


Lesenswert?

Hi!

Nur der Vollständigkeit halber:

mit

_attribute_((naked)) SIGNAL(...)
{
  asm volatile("jump 0x0002");
}

können die Interruptvektoren "verbogen" werden. Das kostet nicht viel
Speicher und ist schön in der C-Datei unterzubringen.
So konnte ich mein Problem wie oben beschrieben lösen...

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.