Jump zum Bootloader beim XMEGA

Gast #4064799
Lesenswert?

Hallo Leute,

ich verwende einen XMEGA256A3U von Atmel. Ich habe bereits einen 
Bootloader geschrieben und diesen auch in den Flash unter der 
Wordadresse 0x20000 hinterlegt. Wenn ich mit den AVRSTUDIO das BOOTRST 
Fuse auf Bootloader stelle, dann startet nicht meine Application sondern 
der Bootloader. Soweit so gut nun zu meinem Problem:

Wie kann ich den Bootloader aus der Application starten. Ich habe 
gelesen das man dies mit einem Extended Indirect Jump (eijmp) macht. 
Dies ist notwendig da ich mehr als 64kByte Flash habe.

ich habe dies im Internet bereits gefunden jedoch hat es nicht 
funktioniert:

asm volatile (
"ldi r30, 0x02" "\n\t" // set up EIND
"sts 60, r30" "\n\t"
"ldi r30,0x00" "\n\t" //indirect call address
"ldi r31,0x00" "\n\t" //16bit address z
"eijmp" "\n\t"

Ich habe daraus eine Funktion erstellet und rufe diese auf.

void Jump_To_Bootloader(void)
{
    #asm
        cli               ; Disable Interrupts
        ldi r30,0x02      ; Set Up EIND to 0x02
        sts 60,r30        ;
        ldi r30,0x00      ; Indirect call adress
        ldi r31,0x00      ; Indirect call adress
        eijmp             ; Jump to bootloader adress
    #endasm
}

Jedoch scheint da ein Fehler drin zu sein. Ich kenne mich leider nicht 
perfekt mit Assembler aus. Vielleicht kann mir jedmand ja einen Hinweis 
geben
Gast #4064815
Lesenswert?

Hi

>Wie kann ich den Bootloader aus der Application starten. Ich habe
>gelesen das man dies mit einem Extended Indirect Jump (eijmp) macht.
>Dies ist notwendig da ich mehr als 64kByte Flash habe.

Wieso? Ein simpler 'jmp' kann 4MWord adressieren.

MfG Spess
Moderator Persönliche Seite #4064996
Lesenswert?

Johann schrieb:
> :-)
>
> Diese Zeile überfordert mich ein wenig :-)

Funktioniert aber.  Ich habe mal den Einsprungoffset weggenommen
(also Einsprung direkt an den Anfang des Laders) und ein typedef
eingeführt:
1
#include <avr/io.h>
2

3
typedef void (*bldr_t)(void) __attribute__((noreturn));
4

5
int main(void)
6
{
7
  const bldr_t jump_to_bootloader = (bldr_t)(BOOT_SECTION_START/2);
8
  EIND = BOOT_SECTION_START>>17;
9
  jump_to_bootloader();
10

11
  return 0;
12
}

Das macht der Compiler draus:
1
.global main
2
        .type   main, @function
3
main:
4
/* prologue: function */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
        ldi r24,lo8(2)
9
        out 0x3c,r24
10
        ldi r30,0
11
        ldi r31,0
12
        eicall
Gast #4065025
Lesenswert?

Danke für die Info Jörg,

aber macht der eicall nicht einen Sprung und legt die Rücksprungadresse 
auf den Stack ab so das er dann wieder zurückspringt und es dann zu 
Problemen kommt?

Kannst Du vielleicht noch etwas zu diesen 2 Zeilen schreiben was diese 
genau machen?

typedef void (*bldr_t)(void) __attribute__((noreturn));

const bldr_t jump_to_bootloader = (bldr_t)(BOOT_SECTION_START/2);
Moderator Persönliche Seite #4065033
Lesenswert?

Johann schrieb:

> aber macht der eicall nicht einen Sprung und legt die Rücksprungadresse
> auf den Stack ab

Ja, macht er.

> so das er dann wieder zurückspringt und es dann zu
> Problemen kommt?

Wer ist „er“?  Wer springt denn da wann zurück?  Dein Bootloader?
Warum sollte er?

Allerdings sollte der Bootloader den Stackpointer (re-)initialisieren.

> Kannst Du vielleicht noch etwas zu diesen 2 Zeilen schreiben was diese
> genau machen?

C-Buch, Kapitel über Zeiger?

> typedef void (*bldr_t)(void) __attribute__((noreturn));

Vereinbart einen Datentypen (namens „bldr_t“) für einen
Funktionszeiger, wobei die Funktion keinen Rückgabewert hat (void),
ja nicht einmal überhaupt zurückzukehren gedenkt (attribute noreturn),
und keine Parameter übernimmt ((void)).

> const bldr_t jump_to_bootloader = (bldr_t)(BOOT_SECTION_START/2);

Bildet einen solchen Zeiger per Typecast aus einer Konstante.
Gast #4065049
Lesenswert?

das ist dann aber schon advance c.

In meinem Bootloader ist eine komplette Intitialisierungsroutine. Jedoch 
bin ich mir gerade nicht sicher ob ich da den Stackpointer zurücksetze 
ich versuche das nachschlagen.

Danke für die Erklärung. Mit Pointer auf Funktionen selber muste ich 
noch nicht arbeiten. Aber ich werde mich da mal im Internet belesen.
Gast #4065099
Lesenswert?

Hi,

sollte es nicht reichen, wenn Johann den Offset in sein Konstrukt 
einbaut und auf die word-address (das Teilen durch 2) umstellt - 
(0x40000 + 0x1FC) / 2 = 0x200FE:

AVR 1916:

Internal firmware request: The user application can decide to start a 
DFU session. This can be achieved by jumping to a specific address in 
the boot loader firmware. The entry point to start a DFU session 
initiated by a user application firmware jump is BOOT_SECTION_START + 
0x1FC for all devices

Also:
1
void Jump_To_Bootloader(void)
2
{
3
    #asm
4
        cli               ; Disable Interrupts
5
        ldi r30,0x02      ; Set Up EIND to 0x02
6
        sts 60,r30        ;
7
        ldi r30,0xFE      ; Indirect call adress
8
        ldi r31,0x00      ; Indirect call adress
9
        eijmp             ; Jump to bootloader adress
10
    #endasm
11
}

Wobei ich bei "word-address" (jetzt auf 0x20000 angepasst) auf extrem 
wackligen Boden stehe ...
Moderator Persönliche Seite #4065162
Lesenswert?

Dieter Frohnapfel schrieb:
> sollte es nicht reichen, wenn Johann den Offset in sein Konstrukt
> einbaut und auf die word-address (das Teilen durch 2) umstellt -
> (0x40000 + 0x1FC) / 2 = 0x200FE:

Zumindest mit dem AVR-GCC nicht: der hat nur 16 bit breite Zeiger.

> Internal firmware request: The user application can decide to start a
> DFU session. This can be achieved by jumping to a specific address in
> the boot loader firmware.

Setzt aber voraus, dass er einen DFU-Bootloader installiert hat.  Ich
habe aber den Eindruck, dass es sein eigener Bootloader ist.
Gast #4065193
Lesenswert?

So Jung ich habe mit dem Debugger das ganze genau untersucht. Wenn ich 
per RS232 ein Kommando an den XMEGA sende dann wird wirklich ein Sprung 
zur Adresse 0x20000 ausgeführt.

Dort steht auch mein Bootloader drin. Dieser wird aber anscheinend nicht 
richtig abgearbeitet. Ich kann mit dem Debugger die Bootsection anzeigen 
dort steht auf jedenfall etwas drin. Nur bleibt der Mikrocontroller dann 
hängen.

Wenn ich jedoch das BOOTRST Fuse auf Bootloader stelle, dann startet der 
Booloader richtig.

Ich verwende eine RS232 Interrupt um die neuen RS232 Flash Daten zu 
empfangen.
#4065225
Lesenswert?

Du solltest bedenken, dass wenn du von der Applikation in den Bootloader 
springst, dass alle Register so bleiben wie sie sind. Also würde ich 
vorher einen Zustand herstellen, der quasi dem Reset entspricht. Also 
alle Interrupts voher deaktivieren, RTC deaktivieren...
Arbeitest du im Bootloader mit Interrupts? Wenn ja musst du den 
ISR-Vektor noch verbiegen, das kannst du über BOOTRST und IVSEL.
Gast #4065230
Lesenswert?

Der Programm Counter bleibt bei 0x2021E stehen. Als wenn es eine 
Endlosschleife ist wie bei While(1)
1
;0000 009A   mikrocontroller_init();
2
   RCALL _mikrocontroller_init
3
;0000 009B 
4
;0000 009C   while(1)
5
_0x15:
6
;0000 009D   {
7
;0000 009E   }
8
  RJMP _0x15
9
;0000 009F }
10
_0x18:
11
  RJMP _0x18
12
;.FEND
Gast #4065250
Lesenswert?

Ich arbeite in der Application Firmware und der Bootloader Firmware mit 
mit Interrupts. Ich habe natürlich nicht den Interruptverktor verbogen 
:-)

Ok dann werde ich IVSEL und BOOTRST konfigurieren.

Das ist doch alles total ... Man ist kurz vor dem Ziel und es geht 
nichts. Das ist heute wieder echt so ein Tag :-(
Gast #4065277
Lesenswert?

Meiner setzt den CLOCk auf 32MHZ konfiguriert einen RS232 Receiver 
Interrupt. In der Interruptroutine werden dann Daten empfangen.

In der RS232 Interruptroutine verwende ich noch einen Hardwaretimer. 
Diesen benutze ich zum Abbruch der RS232 Routine falls nicht genügend 
Daten kommen. Der Timer benutzt aber keine Interruptroutine.

Das Hauptprogramm ist das While(1)

Ich verstehe Atmel nicht. Ich könnte ja einfach aus der Application 
herraus das BOOTRST Fuse verändern anschließnd einen Reset auslösen und 
alles wäre wunderbar. Das hätte mich nur wenige Minuten gekostet jetzt 
glüht mein Kopf und nichts geht.
Moderator Persönliche Seite #4065290
Lesenswert?

Johann schrieb:
> Ich könnte ja einfach aus der Application herraus das BOOTRST Fuse
> verändern anschließnd einen Reset auslösen und alles wäre wunderbar.

Der Sinn einer Fuse ist es aber, nun gerade nicht durch die
Applikation änderbar zu sein.

Nur, weil du gerade deine Software nicht im Griff hast, brauchst du
jetzt nicht die Schuld beim Hardwarehersteller suchen …

Es steht dir ja völlig frei, die Fuse immer zu setzen und bei Bedarf
einfach per Watchdog einen Reset zu veranlassen.  Dann hast du das
Problem halt andersrum, der Bootloader muss dann entscheiden, wann
und wie er die Applikation anspringen will.
Moderator Persönliche Seite #4065337
Lesenswert?

Johann schrieb:
> Der Programm Counter bleibt bei 0x2021E stehen. Als wenn es eine
> Endlosschleife ist wie bei While(1)

Ist es ja auch, das ist deine while(1)-Schleife aus main().  Was
sollte denn in dieser sonst noch passieren?

Alles, was passiert, muss ja über einen Interrupt passieren.

Am Einsprungpunkt des Codes erfolgt ein Stück Initialisierung,
dann ein Sprung auf Byteadresse 0x4043a:
1
2
  (Hier wird übrigens SP initialisiert)
3
   402a8:       ef ef           ldi     r30, 0xFF       ; 255
4
   402aa:       ed bf           out     0x3d, r30       ; 61
5
   402ac:       ef e5           ldi     r30, 0x5F       ; 95
6
   402ae:       ee bf           out     0x3e, r30       ; 62
7
   402b0:       c0 e0           ldi     r28, 0x00       ; 0
8
   402b2:       d0 e3           ldi     r29, 0x30       ; 48
9
   402b4:       1c 94 1d 02     jmp     0x4043a ;  0x4043a

Dort steht offenbar main():
1
   4043a:       d7 d1           rcall   .+942           ;  0x407ea
2
   4043c:       ff cf           rjmp    .-2             ;  0x4043c
3
   4043e:       ff cf           rjmp    .-2             ;  0x4043e

Die zweite Endlosschleife scheint dein Compiler (welcher auch immer
es ist, hast du uns nicht verraten) nach main() hinzuzufügen, die
erste ist deine eigene.

0x407ea ist die Init-Routine, die dein Compiler auch stur so wie
geschrieben aufdröselt:
1
   407ea:       2a de           rcall   .-940           ;  0x40440
2
   407ec:       39 de           rcall   .-910           ;  0x40460
3
   407ee:       7a de           rcall   .-780           ;  0x404e4
4
   407f0:       45 df           rcall   .-374           ;  0x4067c
5
   407f2:       4b df           rcall   .-362           ;  0x4068a
6
   407f4:       82 df           rcall   .-252           ;  0x406fa
7
   407f6:       a3 df           rcall   .-186           ;  0x4073e
8
   407f8:       78 94           sei
9
   407fa:       08 95           ret
Gast #4065360
Lesenswert?

So ich habe mir das List-File noch mal genauer angeschaut.

Dort steht in Adresse 0x020000 der Resetvector vom Bootloader. Demnach 
habe ich mir die Resetroutine angeschaut. Dort wird auf jedenfall das 
IVESL Bit 1 1 gesetzt und somit Interrupt Vector im Bootbereich 
platziert.

Durch den Debugger kann ich auch feststellen das ich im Bootloadebereich 
bin. Jedoch wenn ich ein Kommando per RS232 Sende komme ich nicht in die 
RS232 Routine vom Booloader.
Moderator Persönliche Seite #4065369
Lesenswert?

Johann schrieb:
> komme ich nicht in die RS232 Routine vom Booloader.

PMIC-Setup?

Ich müsste mir die Details für dieses Teil jetzt im Datenblatt
durchwälzen.  Das wäre aber potenziell ein Unterschied zwischen dem,
was dir deine Applikation beim Einsprung in den Bootloader hinterlässt
und dem, was du beim Anspringen des Bootloaders per Reset hast.

Ich würde wohl nach möglichster Möglichkeit versuchen, in einem
Bootloader nur minimal an der Hardware herumzufummeln (nur das
wirklich für den Loader nötige) und ohne Interrupts auszukommen. Ob
du nun da in einem while(1) im main() Däumchen drehst oder an der
Stelle den UART-Rx pollst, bleibt sich schließlich gleich.
Gast #4065387
Lesenswert?

Wenn mit AVR das Fuse-Bit BOOTRST veränder, so das er aus dem Bootloader 
startet (beim Power Up) dann funktioniert ja auch die RS232 
Interruptroutine. Ich kann dann auch wieder zurückspringen auf dem 
Application bereich und auch dort funktioniert dann die RS232-Routine.

Ich werde mal eine LED Toggeln wenn ich im Bootloader bin. Und eine 2 
LED einschalten wenn ich im Bootloader in die RS232 Routine kommen. --> 
Oldscool halt.
Gast #4065409
Lesenswert?

Ok ich habe den Code geändert. Wenn die XMEGA Bootloader Initialisierung 
abgeschlossen ist schalte ich eine LED an. Diese leuchtet auch. Demnach 
muss der XMEGA vollständig initialisiert sein. Jedoch wenn ich einen 
RS232 Befehl sende wird die 2. LED in der USART Interruptserviceroutine 
nicht gesetzt.

Dies ist schon ein Hinweis das mit dem Interrupt etwas nicht in Ordnung 
ist. Da habe ich wenigstens etwas wo ich den Fehler suchen kann.
Gast #4065499
Lesenswert?

Ich konnte leider nichts finden.

Was mir jedoch aufgefallen ist. Wenn ich aus der Application den Jump in 
den Bootloader mache, dann komme ich auch in den Bootoader rein und eine 
LED wird angeschaltet.

Wenn ich jedoch anschließend die Betriebsspannung vom Board entferne und 
anschließend diese wieder aktiviere, dann bin ich wieder im Booloader 
und die Interruptserviceroutine von der USART funktioniert und alles 
geht :-(

Nach einen Power Up sollte doch die Applikation wieder starten und nicht 
der Bootloader. Das BOOTRST Fuse Bit steht auf Application und wird auch 
nicht von mir verändert.
Gast #4065611
Lesenswert?

So ich HABE DEN FEHLER GEFUNDEN:

Ich habe aus der USART_INTERRUPT_SERVICE_ROUTINE den Sprung zum 
Bootloader gemacht. Das scheint so nicht zu gehen oder ich habe etwas 
nicht richtig gelöscht.

Wenn ich in der Interrupt Service Routine nur ein globales Flag setze 
und dieses in der Mainroutine abfrage und dann beim gesetzten Flag den 
Sprung zum Bootloader ausführe dann geht es auch sofort.

Mir ist jedoch aufgefallen das wenn ich im Bootloader bin und die 
Versorgungspannung ausschalte und anschließend wieder Einschalte bootet 
der Mikrocontroller immer automatisch aus dem Booloaderbereich. Ist das 
eine Sicherheitsfunktion so das ich aktive erst wieder den 
Bootloaderbereich verlassen muss?

Dies ist eigentlich sehr gut, denn wenn beim Flashen der Strom ausfällt 
könnte ich in aus der Application herraus nicht mehr in den Bootloader 
springen, da die Application ja bereits gelöscht ist und nur bis zu 
einem gewissen Prozentsatz neu geflasht wurde.
#4065782
Lesenswert?

Solange du in einer ISR bist sind globale Interrupts aus (ISRs sind 
normalerweise nicht unterbrechbar)

Und wenn du aus der ISR einen Sprung machst, denkt der Controller das du 
immer noch in der ISR bist.
Der ASM-Befehl RETI aktiviert beim Beenden der ISR wieder die globalen 
Interrupts.

Ich kenne mich mit den XMegas nicht aus, die haben ja so ein tolles 
Eventsystem.


Bei den normalen Megas, sollte eigentlich ein sei() reichen, was du 
vermutlich in deinem Bootloadercode auch tust.

Vor deinem Sprung sollte man alle ISRs deaktivieren.


Und noch was, eigentlich ist die elegante Methode einen Reset 
auszulösen, den Watchdog ablaufen zu lassen. Danach wird der Controller 
auch Hardwareseitig komplett neu initialisiert.
Du hast ja geschrieben das bei einem Reset bei dir zuerst der Bootloader 
startet.
(Firma: TravelRec.) Persönliche Seite #4066371
Lesenswert?

Christian K. schrieb:
> Solange du in einer ISR bist sind globale Interrupts aus (ISRs sind
> normalerweise nicht unterbrechbar)

Nicht beim XMEGA. Dort kann ein höher priorisierter Interrupt einen 
niedriger priorisierten unterbrechen. Aber Du hast schon Recht, 
irgendwann muss schon ein passendes reti kommen, um die noch laufende 
ISR abzuschließen.
Gast #4066542
Lesenswert?

Mein Resetvektor im Bootloader verbiegt ja auch die Interrupttabelle. 
Aber über welchen Befehl kann ich dann das RETI löschen so das mein 
Bootloader nicht mehr denkt das er noch im Interrupt ist?
#4066612
Lesenswert?

Ein RETI macht das gleiche wie ein RET, oberstes Element des Stacks in 
den PC laden und zusätzlich das I-Flag im SREG setzen.

Ich konnte jetzt keine Informationen finden ob das RETI bei einem XMega 
mehr macht.

Da ein XMega ein Interrupt Controller hat, würd ich dir mal raten das 
Disassembly einer ISR anzuschauen, um zu checken ob am Ende der ISR vor 
dem RETI noch irgendwelche anderen Registerbits verändert werden.
Moderator Persönliche Seite #4066756
Lesenswert?

Johann schrieb:
> Aber über welchen Befehl kann ich dann das RETI löschen so das mein
> Bootloader nicht mehr denkt das er noch im Interrupt ist?

Warum zum Geier™ tust du nicht endlich mal, was ich dir die ganze
Zeit lang predige, und guckst dir das Kapitel zum Interruptcontroller
im Datenblatt an?  Ich mein', ich klapp' das Ding auf, und es guckt
mich ein Statusregister an, bei dem sowas steht:
1
Bit 0 ­ LOLVLEX: Low-level Interrupt Executing
2
This flag is set when a low-level interrupt is executing or when the interrupt handler has been interrupted by an
3
interrupt from higher level or an NMI. The flag will be cleared when returning (RETI) from the interrupt handler.
#4066803
Lesenswert?

Gut zu wissen das ein RETI bei einem XMega mehr macht.

Im Offiziellen "AVR Instruction Set Manual" und im Kapitel Instruction 
Set Summary eines XMegas-Datenblatts gibt es keinen Verweis darauf.

@Johann
Jetzt hab ich mir mal das Datenblatt vom Xmega A3U geladen, aber ich 
find die zitierte Passage nicht. Auch die Volltextsuche bring nichts.


EDIT:
Atmel hat bei den Datenblättern der XMegas outgesourced.
Im Xmega AU hab ich es gefunden.
Moderator Persönliche Seite #4066850
Lesenswert?

spess53 schrieb:
> Die interessanten Sachen stehen im entsprechenden Manual:

Ja, diese doppelten Datenblätter sind Scheibenkleister.  Wenn sie
wenigstens im per-device datasheet dann die unnötigen Kapitel-stubs
gleich weggelassen hätten, aber so erwecken die Dinger immer noch
den Eindruck, als würden sie alles abdecken, obwohl man in Wirklichkeit
darin nur einige ganz wenige Details findet, während der große Rest im
Familien-Datenblatt steht.

Aber wenn man irgendwie ernsthaft was mit einem Xmega macht, sollte
man das eigentlich kennen.

Christian K. schrieb:
> Im Offiziellen "AVR Instruction Set Manual" und im Kapitel Instruction
> Set Summary eines XMegas-Datenblatts gibt es keinen Verweis darauf.

Es gibt auch keinen Hinweis drauf, dass bspw. ein OUT auf ein
bestimmtes Portregister ein Pin toggeln kann oder ein Interruptflag
löschen.

Spätestens, wenn man aber mal irgendwie versucht nachzudenken, wie sie
die Interruptpriorisierung gelöst haben können, wird einem klar, dass
der Interruptcontroller auf irgendeine Weise wissen muss, dass gerade
eine ISR in Bearbeitung ist, denn nur so kann er verhindern, dass bei
global zugelassenen Interrupts ein Interrupt gleicher oder niederer
Priorität die aktuelle ISR unterbricht.
#4342664
Lesenswert?

Hallo Leute,

ich versuche im Moment den Bootloader mit der Version 1.04 auf meinem 
AtxMega63A3U zum Laufen zu bekommen.

Ich möchte den bootloader aus meiner Applikation starten.
Die Abfrage des IOs zum Starten des Bootloaders habe ich ausgebaut.
So dass dieser starten sollte. Kompiliert habe ich mit IAR.

Die Fusebits habe ich auf die Applikaiton gestellt.

In meiner Applikation benutze ich die USB-Schnittstelle um mit dem PC zu 
kommunizieren.

Mir fehlt jetzt noch der Jump aus der Applikation in den Bootloader.
Ich habe zwar schon einiges hier gelesen aber ich kriege es nicht hin.

Hat jemand dies schon zuverlässig lösen können?

Danke.

Gruß,
Georg.
Dieser Beitrag ist gesperrt und kann nicht beantwortet werden.