Forum: Mikrocontroller und Digitale Elektronik Bootloader STMF413VGT6


von Curby23523 N. (Gast)


Lesenswert?

Hallo,

verstehe ich es richtig, dass durch die Boot Pins veranlasst wird, dass 
der System Memory in 0x1fff0000 - 0x1fff77ff auf die Adresse 0x00000000 
gemappt wird?

Heißt das, ich kann meinen Bootloader wie ein gewöhnliches Programm 
schreiben und muss es nur an die Position 0x1fff0000 schreiben und aus 
Sicht des µC ist es das normale Programm an Adresse 0x00? Inkl. 
Interrupt Vectoren?

Dann muss ich, wenn der Bootloader beendet wird, dem µC ja auch 
mitteilen, dass er jetzt an Adresse 0x00 ohne Bootpin starten soll oder?

Führe ich dann den Code ab Adresse 0x08000000 aus, inkl. verbiegen der 
Interrupt Vectoren? Oder resete ich quasi irgendwie den µC, dass jetzt 
aber nicht der System Memory gemappt wird? Ich glaube über SYSCFG kann 
ich im Betrieb das Mapping wieder ändern?

im NVIC gibt es ja ein Register zum mappen der IRQ. Die Positionierung 
des Bootloaders im Flash mache ich bestimmt mit einem Linkerkommando 
oder gleich das Linker-Script?

Mein Prozessor ist ein STM32F413VGT6.

Danke für eure Hilfe!

von pegel (Gast)


Angehängte Dateien:

Lesenswert?

Nils N. schrieb:
> und muss es nur an die Position 0x1fff0000 schreiben

Zeig mal, wenn du das geschafft hast ;)

von Curby23523 N. (Gast)


Lesenswert?

Wo soll das Problem sein? 0x1fff0000 ist doch System memory (30kb) ...?

von pegel (Gast)


Lesenswert?

Steht das etwas von Flash? Nein.
Und einen ROM zu beschreiben ist schwierig.

von pegel (Gast)


Lesenswert?

Steht da etwas von Flash?

sollte es natürlich heißen

von Martin B. (ratazong)


Lesenswert?

Ich habe mal einen Bootloader für stm32f411 (mit CubeMX und gcc) 
geschrieben. In der CubeMX repository befindet sich ein Beispiel für 
einen Bootloader.

bei mir (Ubuntu) wars zu finden unter:

~/STM32Cube/Repository/STM32Cube_FW_F4_V1.16.0/Projects/STM324xG_EVAL/Ap 
plications/LwIP/LwIP_IAP


Das war aber tückisch, habe mehrere Tage debuggen müssen, um 
herauszufinden was am User Programm dann zu beachten ist.


Bootloader wird als normales Programm geschrieben. Startadresse 
0x8000000.

zu ladendes Anwendungsprogramm: Startadresse bei mir 0x8010000
dann den linkerfile entsprechend anpassen
und jetzt kommt das merkwürdige:
im sourcefile system_stm32f4xx.c, das von CubeMX generiert wird, musst 
Du noch einen offset anpassen

Zeile suchen:
#define VECT_TAB_OFFSET 0x00

jetzt umdefinieren in 0x10000 (bei mir startete das User programm bei 
0x8010000, musst Du entsprechend anpassen)

Wenn Du das nicht machst laufen keine interrupts mehr, nur blinky geht.
Das Ganze wird in einer UM17xx von STM beschrieben. Was die sich bei STM 
dabei gedacht haben ist mir allerdings schleierhaft. Startadresse des 
Anwendungsprogramms als offset (!) im Anwendungsprogramm hardcoded in 
der source notwendig. Portabel ist anders.

von pegel (Gast)


Lesenswert?

Das Thema hatten wir doch schon mal.

Es wäre besser das dem CubeMX Schöpfer mitzuteilen.

Weiß nicht mehr genau wo, aber der hat in einem Beitrag auf Anfragen und 
Kritik zu CubeMX reagiert und die Vorschläge in der nächsten Version 
eingebracht.

von Martin B. (ratazong)


Lesenswert?

pegel schrieb:
> Es wäre besser das dem CubeMX Schöpfer mitzuteilen.

Oh, das wird dann aber ein längerer Erguss.
Bin recht enttäuscht von CubeMX. Idee ist ja gut, aber performance . . .

von pegel (Gast)


Lesenswert?

Ich finde, über die Versionen gesehen, hat es sich aber schon recht gut 
entwickelt.

Und mindestens einer ist aktiv dran.

von Curby23523 N. (Gast)


Lesenswert?

Wenn ich den vorprogrammierten Bootloader nicht verändern kann verstehe 
ich es so, dass ich meinen Bootloader ganz normal auf den Prozessor 
lade.

Für diesen sind z.B. die ersten 16kByte reserviert. Das eigentliche 
Programm startet erst ab der Adresse 0x4000.

Wenn der Bootloader, welcher dann ja immer zuerst startet ab Adresse 
0x00, entscheidet, dass das Hauptprogramm gestartet werden soll, springt 
dieser zur Adresse 0x4000.

Das Hauptprogramm muss dann nur noch die IRQ Tabelle umbiegen. Geht 
diese Vorangehensweise?

von stromverdichter (Gast)


Lesenswert?

Ja, so geht das schon. Die Linkerfiles müssen halt zusätzlich passen, 
für Bootloader und Applikation.
Interrupts vor dem Sprung abschalten und den Watchdog zumindest länger 
stellen hat mir geholfen.

von eagle user (Gast)


Lesenswert?

Im Prinzip ja.
Bootloader und Programm müssen auf die Adressen 0x08000000 bzw. 
0x08004000 gelinkt werden müssen. Die Adressen 0x0 und 0x4 werden nur 
unmittelbar nach dem Reset von der CPU benutzt um SP und PC zu 
initialisieren. 0x4000 wird nicht benutzt.

Der Bootloader muss nicht zu 0x4000 und auch nicht zu 0x08004000 
springen, sondern zu der Adresse, die in 0x08004004 steht.

Der Stack Pointer muss mit dem Inhalt von 0x08004000 geladen werden. Das 
kann der Bootloader oder die crt0 des Hauptprogramms machen. Wenn du 
eine vorgefertigte crt0 benutzt, solltest du das heraus finden, weil, 
wenn die das macht, kannst du den Bootloader zu 100% in C schreiben.

Die Vektortabelle des Hauptprogramms kann irgendwo stehen, sie muss nur 
auf 512-Byte aligned sein (oder 1024- oder 2048-Byte, je nach NVIC). Das 
Hauptprogramm könnte sogar die vom Bootloader mit benutzen wenn der 
keine Interrupts braucht.

von ratazong (Gast)


Lesenswert?

stromverdichter schrieb:
> Interrupts vor dem Sprung abschalten und den Watchdog zumindest länger
> stellen hat mir geholfen.

würde ich schärfer formulieren. (Alle Einträge in der IVC bekommen ja 
praktisch neue Behandlungsroutinen)
Alles, was Du vor dem Sprung an Hardware initialisiert hast, wieder 
deinitialisieren. Dann erst springen.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

http://www.springer.com/de/book/9783658148492 ,

runterscrollen auf Zusatmaterial, herunterladen, Kapitel 9.

Beschreibung der Internals im Buch.

Man kann den internen BL für sich mitnutzen, sehe ich aber äusserst 
selten. Normalerweise ist der gelieferte Bootloader resident und per 
Jumper aktivierbar, um im Notfall (zerschossener Applikationsbootloader) 
eine Notfallebene zu haben sowie der Produktion eine Hilfe zum Flashen 
des Urimages zu geben. Ist äusserst praktisch!

Frohe Feiertage!

von Curby23523 N. (Gast)


Lesenswert?

Prima, dann werde ich erstmal diesen Weg begehen und schauen, wie weit 
ich komme.

von W.S. (Gast)


Lesenswert?

Nils N. schrieb:
> Dann muss ich, wenn der Bootloader beendet wird, dem µC ja auch
> mitteilen, dass er jetzt an Adresse 0x00 ohne Bootpin starten soll oder?
>
> Führe ich dann den Code ab Adresse 0x08000000 aus, inkl. verbiegen der
> Interrupt Vectoren?

Deinen normalen Code kannst du getrost ab Adresse 0 beginnen lassen. 
Alles was im eingebauten User-Flash (ab 0x08000000) steht, ist auch ab 
Adresse 0 les- und ausführbar.

Lediglich dann, wenn man den eingebauten Bootlader benutzen will und 
deshalb die "BOOT"-Pin(s) beim Reset entsprechend setzt, wird der 
Bootlader ab Adresse 0 eingeblendet. Dann kann logischermaßen auf den 
Flash nur über den Adreßbereich ab 0x08000000 zugegriffen werden.

Aber im Normalfall brauchst du dich um den Bereich ab 0x08000000 nicht 
zu kümmern. Deinen Code kannst du getrost ab 0 linken.

W.S.

von eagle user (Gast)


Lesenswert?

W.S. schrieb:
> Deinen normalen Code kannst du getrost ab Adresse 0 beginnen lassen.
> Alles was im eingebauten User-Flash (ab 0x08000000) steht, ist auch ab
> Adresse 0 les- und ausführbar.

> Aber im Normalfall brauchst du dich um den Bereich ab 0x08000000 nicht
> zu kümmern. Deinen Code kannst du getrost ab 0 linken.

Was wäre der Vorteil vom Code ab 0? Normalerweise will man auf Adressen 
unterhalb von 0x08000000 garnicht zugreifen und Zugriffe darauf per MPU 
verbieten.

Der eingebaute Bootloader hat zwei entscheidende Vorteile: das Flashen 
funktioniert mit jedem Chip, das Programm auf dem PC muss nicht die 
verschiedenen Flash-Algorithmen kennen. Und man kann ggf. den 
SWD-Stecker weglassen und die Pins für etwas nützliches nutzen.

Also, ich bleibe bei 0x08000000...

von Curby23523 N. (Gast)


Lesenswert?

Ich nochmal. Wenn ich mit folgendem Code ein Offset von 10 für die IRQ 
Tabelle vorgebe und dann an die Adresse 0x0800000A mein Programm flashe, 
wird dieses nicht korrekt ausgeführt. Auch wenn ich gar keine Interrupts 
verwende.

Was mache ich da falsch? Ich mache vorher einen Chiperase, es sollte 
dann ja überall vorher 0xFF stehen.

von ratazong (Gast)


Lesenswert?

IRQ

Nils N. schrieb:
> Ich nochmal. Wenn ich mit folgendem Code ein Offset von 10 für die
> IRQ
> Tabelle vorgebe und dann an die Adresse 0x0800000A mein Programm flashe,
> wird dieses nicht korrekt ausgeführt. Auch wenn ich gar keine Interrupts
> verwende.
>
> Was mache ich da falsch? Ich mache vorher einen Chiperase, es sollte
> dann ja überall vorher 0xFF stehen.

Der Offset braucht immer ein bestimmtes alignment. Hängt vom konkreten 
chip ab.

Sprich Offset von 10 geht nicht. Beim STM32F411 sind es z.B. 512 byte 
alignment. Also Offset muss durch 512 teilbar sein.

von Curby23523 N. (Gast)


Lesenswert?

Ok, das sehe ich auch grade, dass der Wert "1" 128 Wörtern entspricht, 
also der Adresse 0x08000100.

Aber auch ohne Benutzung der Interrupts wird mein Code nicht mehr 
ausgeführt, wenn ich diesen an 0x08000100 schreibe und vorher den Flash 
lösche. Es handelt sich um ein simples LED-Hello World.

von ratazong (Gast)


Lesenswert?

Nils N. schrieb:
> Ok, das sehe ich auch grade, dass der Wert "1" 128 Wörtern
> entspricht,
> also der Adresse 0x08000100.
>
> Aber auch ohne Benutzung der Interrupts wird mein Code nicht mehr
> ausgeführt, wenn ich diesen an 0x08000100 schreibe und vorher den Flash
> lösche. Es handelt sich um ein simples LED-Hello World.

Ok,

raffe gerade nicht, was Du gerade probierst.

Wenn der STM aus dem reset kommt erwartet er eine Sprungtabelle ab 
Adresse 0x8000000. Das ist die Sprungtabelle Deines bootloaders.

Dein Anwendungsprogramm liegt beispielweise bei Adresse 0x8010000. Dort 
brauchst Du die Sprungtabelle Deines Anwendungsprogramms. Wenn Du das 
lädst, darfst Du den bootloader natürlich nicht löschen. Also keinen 
chip erase machen. Sondern nur den Teil des flash löschen, den dein 
Anwendungsprogramm braucht. Den Teil sollte Dein bootloader Programm 
bewerkstelligen.

von Curby23523 N. (Gast)


Lesenswert?

Ich versuche ledichlich erstmal ein Programm an eine andere Adresse zu 
schreiben, manuell ohne irgendeinen Bootloader.

LED-Hello World Programm an Adresse 0x08000100 schreiben.

Der STM32 bootet und mappt 0x08000100 an Adresse 0x00000000. Dann kommt 
an Adresse 0x00 bis 0xff nur "nop" und dann das Programm. Ich benutze da 
noch gar keine Interrupts, aber bereits das LED blinken wird nicht 
ausgeführt.

von STM Apprentice (Gast)


Lesenswert?

Nils N. schrieb:
> LED-Hello World Programm an Adresse 0x08000100 schreiben.

Das alleine reicht nicht. Damit es funktioniert muss es auch
für diese Basisadresse compiliert bzw gelinkt sein.

von ratazong (Gast)


Lesenswert?

STM Apprentice schrieb:
> Nils N. schrieb:
>> LED-Hello World Programm an Adresse 0x08000100 schreiben.
>
> Das alleine reicht nicht. Damit es funktioniert muss es auch
> für diese Basisadresse compiliert bzw gelinkt sein.

das reicht auch noch nicht.

An den ersten Stellen ist nämlich kein ausführbarer Code, sondern eine 
Tabelle. Die fängt an mit dem Inhalt des Stackpointers, dann der Adresse 
des ausführbaren Programms, dann interrupt Vektoren.

Ich befürchte, TO muss noch einiges lesen.

Wenn die CPU aus dem Reset kommt, erwartet sie vernünftige Werte an der 
Stelle 0x8000000. Es sei denn, man benutzt die internen Bootloader des 
STM

von A.. P. (arnonym)


Lesenswert?

Also:

In den Systemspeicher (also an die Stelle des internen Bootloaders) 
kannst du nicht schreiben, da es ein Nur-Lese-Speicher (sprich ROM) ist. 
Der interne Bootloader ist für alle STM32-Controller fest 
einprogrammiert.

Einen eigenen Bootloader kompilierst du vorzugsweise für die übliche 
Flash-Startaddresse 0x08000000. In diesem Bootloader machst du zunächst 
deine ganze Boot-Magie (z.B. Systemcheck, Application-Image verifizieren 
o.ä.), sofern notwendig. In diesem Bootloader definierst du bereits den 
Offset zur Startaddresse der Vektortabelle deines Anwendungsprogramms 
(z.B. 0x08004000). Wenn ich mich recht erinnere, wird VECT_TAB_OFFSET 
relativ zur Basisaddresse angeben, hier also 0x4000. Beachten musst du 
hierbei, dass die Vektortabelle immer an einer bestimmten Bytegrenze 
ausgerichtet sein muss (je nach Controller bei Vielfachen von 512, 1024 
oder 2048 Byte; das musst du im Datenblatt nachsehen). Bevor nun der 
Sprung zum Anwendungsprogramm erfolgt, musst du sämtliche initialisierte 
Peripherie deinitialisieren sowie alle Interrupts abschalten. Das 
Anwendungsprogramm soll ja ein System vorfinden, so als ob vorher nie 
was anderes gelaufen ist (quasi ein vorgegaukelter Kaltstart).

Dein Anwendungsprogramm entwickelst du nun, wie du es sonst auch gewohnt 
bist, mit einer Änderung: in deinen Linker-Einstellungen musst du dein 
Programm nun bei der vorher festgelegten Startaddresse 0x08004000 
starten lassen und natürlich die verfügbare Flash-Größe um diese 0x4000 
Bytes reduzieren.

Nun noch den Bootloader an die Addresse 0x08000000 und dein 
Anwendungsprogramm fortan an 0x08004000 flashen und fertig ist die 
Laube. Natürlich solltest du beim Chip Erase darauf achten nicht die 
Sektoren mit deinem Bootloader mitzulöschen ;)

Gruß

von Curby23523 N. (Gast)


Lesenswert?

A.. P. schrieb:
> Dein Anwendungsprogramm entwickelst du nun, wie du es sonst auch gewohnt
> bist, mit einer Änderung: in deinen Linker-Einstellungen musst du dein
> Programm nun bei der vorher festgelegten Startaddresse 0x08004000
> starten lassen und natürlich die verfügbare Flash-Größe um diese 0x4000
> Bytes reduzieren.

Wenn ich mein LED Blink Programm, welches noch komplett ohne Interrupts 
arbeitet über das Linkerscript von 0x8000000 auf 0x8000100 
umpositioniere arbeitet es nicht, sprich: kein LED blinken. Auch nicht 
beim vorherigen Chip erase.

Im generierten Hex sehe ich auch, dass nun korrekt alles an 0x8000100 
gelinkt wurde.

Sollte der Prozessor bei einem vorherigen Chiperase nicht bis 0x8000100 
durchgehen und dann mein korrekt gelinktes Programm ausführen, z.B. wenn 
der Bootloader noch gar nicht programmiert ist?

Meinn Programm ist klein und simpel (noch für einen STM32F407, weil ich 
dafür ein Discoveryboard hoer habe):
1
#include "stm32f4xx.h"
2
3
#define T_OUTPUT 0x01
4
#define MODE(a,b) ((b)<<(2*a))
5
#define SET(a) (1<<(a))
6
7
#define F_CPU 16000000
8
      
9
volatile static uint16_t temp = 0;
10
11
int main(void){
12
  //SCB->VTOR = (1UL<<9);
13
14
  RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
15
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
16
17
  GPIOD->MODER |= MODE(12, T_OUTPUT);
18
  GPIOD->MODER |= MODE(13, T_OUTPUT);
19
  GPIOD->MODER |= MODE(14, T_OUTPUT);
20
  GPIOD->MODER |= MODE(15, T_OUTPUT);
21
22
  TIM6->PSC = 256;
23
  TIM6->ARR = F_CPU/256;
24
  TIM6->CR1 |= TIM_CR1_CEN;
25
26
  while(1){
27
    if(TIM6->SR & TIM_SR_UIF){
28
      TIM6->SR &= ~TIM_SR_UIF;
29
      GPIOD->ODR ^= SET(14);
30
      GPIOD->ODR ^= SET(15);
31
    }
32
  }
33
}

Ich habe auch mal ein zweites Programm zusätzlich reinprogrammiert als 
Bootloader Ersatz, welches einfach nur an die Adresse 0x8000100 springen 
soll. Das geht leider auch nicht:
1
#include "stm32f4xx.h"
2
void (*start)(void) = 0x8000100;
3
4
int main(void)
5
{
6
  start();
7
  for(;;);
8
}

von Martin B. (ratazong)


Lesenswert?

>>Sollte der Prozessor bei einem vorherigen Chiperase nicht bis 0x8000100
>>durchgehen und dann mein korrekt gelinktes Programm ausführen, z.B. wenn
>>der Bootloader noch gar nicht programmiert ist?

So ein Verhalten kenne ich nur vom AVR. Da ist zufällig(?) 0xff ein NOP.

Das ist beim ARM komplett anders. Der erwartet ab 0x8000000 eine 
Tabelle, keinen ausführbaren Code.

Bei Deinem Beispiel ab 0x8000000 stehen erstmal 0xff macht der 
folgendes:

er lädt 0xffffffff in sein Stackpointer (aber da ist gar kein RAM)
dann lädt er 0xffffffff in ein Register und springt an diese Adresse 
0xffffffff, (aber da ist gar kein Code.)


So springe ich aus dem bootloader in das applikationsprogramm und das 
funzt
/snip
        /* Jump to user application */
        JumpAddress = *(__IO uint32_t*) (MY_PROGRAM_START_ADDRESS + 4);
        Jump_To_Application = (pFunction) JumpAddress;
        /* Initialize user application's Stack Pointer */
        __set_MSP(*(__IO uint32_t*) MY_PROGRAM_START_ADDRESS);
        Jump_To_Application();
        /* do nothing */
        while(1);
/snap

Der Code holt sich aus dem Applikationsprogramm die Adresse für den 
Stackpointer und die Einsprungpunkt für das Programm und springt dann 
dahin.
Falls Dein Applikationsprogramm den VTOR nicht setzt, musst Du den auch 
noch vor dem JumpTo_Application() setzen.

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.