Forum: Mikrocontroller und Digitale Elektronik STM32F407 Sprung zu Programm im anderen Sector


von Franz Sauerdorf (Gast)


Lesenswert?

Liebe Mikrocontroller-Gemeinde!
Ich habe einen STM32F4 und habe im Sector 0 sowie im Sector 1 jeweils 
ein Programm. Nun möchte ich vom Sector 0 in den Sector 1 springen.
Meine Recherche hat ergeben, dass die neue Adresse in die Vectortabelle 
geschrieben werden muss und anschließend kann der Sprung erfolgen. Dazu 
soll in dem SCB->VTOR Register geschrieben werden. Leider finde ich 
weder im Datenblatt noch im Reference Manual des STM32F4 etwas darüber.
Kann mir jemand einen Anstoß geben, wie man das realisiert?
Muss ich "hinter" mir auch noch aufräumen, also den Ram und ggf. 
Peripherie freigeben oder wird dies durch den Programmaufruf automatisch 
zurückgesetzt?
Die Funktion NVIC_SetVectorTable kennt mein Eclipse leider nicht, ist 
diese nicht für den STM32F4 verfügbar? Ich konnte diese auch nicht in 
den Libs finden. Ich benutze jedoch u.a. NVIC_SystemReset, welche 
gefunden wird.

Liebe Grüße
Franz

von hp-freund (Gast)


Lesenswert?

Das ist eine allgemeine CMSIS Frage:
http://elk.informatik.fh-augsburg.de/pub/stm32lab/libs/CMSIS-4.2/CMSIS/Documentation/Core/html/_using__v_t_o_r_pg.html

Bezieht sich zwar auf den Sprung in den RAM, das Prinzip stimmt aber.

von Franz Sauerdorf (Gast)


Lesenswert?

Hallo hp-freund,
danke für den Link. Leider hilft mir das mangels Dokumentation nur 
bedingt weiter.
Momentan versuche ich:
1
__disable_irq();
2
SCB->VTOR = (0x08000000 | 0x4000);
Dann hängt er sich auf.

Leider habe ich kaum einen Ansatz, den ich weiter verfolgen könnte.

von Nop (Gast)


Lesenswert?

Franz Sauerdorf schrieb:
> Dann hängt er sich auf.

Damit legst Du ja auch nur die Vektortabelle in den zweiten Sektor - 
aber deswegen springt er dort ja nicht gleich hin? Ich würde erwarten, 
daß er stattdessen im Quelltext nach Deiner Zeile weitermacht.

von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

Schau Dir an, wie das Bootloader fuer den STM32 machen, z.b. in dem 
Blackmagic Debug Probe https://github.com/blacksphere/blackmagic.git.

von Franz Sauerdorf (Gast)


Lesenswert?

Ja, richtig. Man muss den Stack laden und dann eine Funktion mit der 
Adresse aufrufen, leider finde ich keinen Ansatz dazu, der mich weiter 
bringt.
Was gutes ist dieser Link: 
http://stackoverflow.com/questions/16490315/stm32f4-memory-jumping

von Nop (Gast)


Lesenswert?

Franz Sauerdorf schrieb:
> Ja, richtig. Man muss den Stack laden und dann eine Funktion mit der
> Adresse aufrufen

Naja den Stack laden, dazu braucht's ein, zwei Zeilen inline-Assembler. 
Wenn die Resetvektortabelle im zweiten Sektor richtig gebaut ist, dann 
findest Du die zu ladende Stack-Adresse an der Adresse 0x08004000. Der 
Resetvektor, der im zweiten Sektor dann angesprungen werden soll, ist 
dann an 0x08004004.

Also:
- VTOR setzen
- ein DSB machen
- 0x08004000 in ein Register laden
- Stackregister mit dem laden, worauf voriges Register zeigt ("[Rx]")
- Register mit 0x08004004 laden
- einen branch auf das machen, worauf voriges Register zeigt ("[Rx]")

So würd ich's jedenfalls erstmal probieren.

von Franz Sauerdorf (Gast)


Lesenswert?

Danke für die Hinweise. Bisher habe ich mir das zusammengebastelt:
1
uint32_t ApplicationAddress = 0x4000;
2
__disable_irq();
3
SCB->VTOR = (0x08000000 | ApplicationAddress);
4
__DSB();
5
__set_MSP(*(__IO uint32_t*)ApplicationAddress); // setze Main Stack Pointer
6
__set_PSP(*(__IO uint32_t*)ApplicationAddress); // setze Process Stack Pointer

Jetzt habe ich noch diesen Ansatz gefunden, der den Sprung realisieren 
soll:
1
static Function jumpToApplication = (Function) * (__IO uint32_t*)(ApplicationAddress + 4);
Leider hilft mir dieser nicht wirklich weiter, da ich nicht weiß, wie 
ich diesen Schnipsel anwenden muss. Da ist auch dein Hinweis mit dem 
Reset-Vektor berücksichtigt.

von Nop (Gast)


Lesenswert?

Franz Sauerdorf schrieb:
> Danke für die Hinweise. Bisher habe ich mir das
> zusammengebastelt:uint32_t ApplicationAddress = 0x4000;

Nee, das geht so nicht.

> __set_MSP(*(__IO uint32_t*)ApplicationAddress); // setze Main Stack
> Pointer

Da dereferenzierst Du dann die Adresse 0x4000, nicht 0x08004000 .

> __set_PSP(*(__IO uint32_t*)ApplicationAddress); // setze Process Stack
> Pointer

Ebenfalls.

> jumpToApplication = (Function) * (__IO uint32_t*)(ApplicationAddress + 4);

Und da dereferenzierst Du 0x4004 statt 0x08004004.

Wenn das aber so klappen würde, dann würdest Du den Sprung einfach mit 
jumpToApplication(); machen. Muß halt die Funktion als void-void 
deklariert sein. Problem: Dabei werden dann schonmal die Register 
gesichert. Deswegen würd ich das als inline-Assembler machen, die paar 
Befehle beißen nicht.

von Franz Sauerdorf (Gast)


Lesenswert?

Also meinst du wäre folgendes besser:
1
void LoadStackAndGo( void* sp, void* entry)
2
{
3
    __asm (
4
        "mov sp, r0 \n"
5
        "mov pc, r1 \n"
6
    );
7
}
Aufrufen dann mit:
1
__disable_irq();
2
SCB->VTOR = (0x08000000 | ApplicationAddress);
3
__DSB();
4
LoadStackAndGo(0x08004000, 0x08004004);

Oder verstehe ich das falsch?

von Nop (Gast)


Lesenswert?

Franz Sauerdorf schrieb:

> Oder verstehe ich das falsch?

Von der Idee her ja, aber prüf nochmal, ob ein MOV mit Ziel PC überhaupt 
geht und man das nicht als branch/jump schreiben muß.

Außerdem muß das jeweils [r0] und [r1] sein, weil einmal dereferenziert 
werden muß. Man will ja nicht den SP auf 0x08004000 setzen, sondern auf 
das, was an der Adresse 0x08004000 steht. PC genauso.

von Franz Sauerdorf (Gast)


Lesenswert?

Danke.
Ich denke das MOV schon das richtige ist. Ich will ja den Pointer 
setzen.
Wenn ich die dereferenzieren möchte mit eckigen Klammern, wirft der 
Compiler fehlt aus.

Laut dem sollte es eigentlich so funktionieren: 
http://stackoverflow.com/questions/16490315/stm32f4-memory-jumping

Leider hängt er sich auf. Hast du noch einen Tipp für mich?

von gnugnu (Gast)


Lesenswert?

hängt es sich auch beim Singlesteppen auf? Was ist direkt vor dem Sprung 
in den Registern?

Du brauchst bei derartigen Dingen in jedem Fall ein ISB wichtiger als 
ein DSB (dazu gibt es von ARM ausführliche Dokumentation).

von Franz Sauerdorf (Gast)


Lesenswert?

Das weiß ich leider nicht genau. Ich programmiere mit Eclipse und habe 
schon 2 Tage (alleine heute 2h) damit verbracht OpenOCD zum Laufen zu 
bringen jedoch ohne Erfolg.

Gibt es andere Tools die du empfehlen kannst? Gerne auch stand-alone, 
also außerhalb von Eclipse, wenn es dafür funktioniert.

von Franz Sauerdorf (Gast)


Lesenswert?

Ich benutze einen STM32F4 Discovery.

Ich habe noch einen anderen Code gefunden, der leider ebenfalls nicht 
funktioniert.
1
typedef  void (*pFunction)(void);
2
pFunction Jump_To_Application;
3
uint32_t JumpAddress, APPLICATION_ADDRESS = 0x08004000;
4
__disable_irq();
5
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS+4);
6
Jump_To_Application = (pFunction) JumpAddress;
7
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
8
Jump_To_Application();

Ich brauche unbedingt eine Möglichkeit zu debuggen... oder den 
ultimativen Code :-)

von Super G. (Gast)


Lesenswert?

So funktioniert es bei mir:
1
typedef  void (*pFunction)(void);
2
3
/* Base address of the Flash sectors */
4
#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000)
5
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000)
6
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000)
7
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000)
8
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000)
9
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000)
10
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000)
11
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000)
12
#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000)
13
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000)
14
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000)
15
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000)
16
17
/* Private function pointer */
18
static pFunction Jump_To_Application;
19
/* Private variables */
20
static uint32_t JumpAddress;
21
22
/* Jump to user application */
23
JumpAddress = *(__IO uint32_t*) (ADDR_FLASH_SECTOR_5 + 4);
24
Jump_To_Application = (pFunction)JumpAddress;
25
/* Initialize user application's Stack Pointer */
26
__set_MSP(*(__IO uint32_t*) ADDR_FLASH_SECTOR_5);
27
Jump_To_Application();

An Adresse 0x08000000 liegt mein Bootloader. Soll kein Bootload-Vorgang 
stattfinden springe ich an Adresse 0x08020000 wo mein Hauptprogramm 
liegt.
Die Funktion "__set_MSP()" befindet sich in der "core_cmFunc.h".

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


Lesenswert?

Hallo Franz:

Du findest für deine Plattform funktionierenden Beispielcode hier:

http://www.springer.com/de/book/9783658148492#otherversion=9783658148508

(runterscrollen zum link "Zusatzmaterial," entpacken und Kapitel 9 
Beispiel nutzen). Eine genau Diskussion findet sich in Kapitel 9 in dem 
Buch.

HTH

von Franz Sauerdorf (Gast)


Lesenswert?

Danke, mein Bootloader liegt ebenfalls bei 0x08000000 und mein 
Hauptprogramm bei 0x08004000. Mein Hauptprogramm ist zur Zeit ein LED 
Beispiel, was ich mit dem ST-Link geschrieben habe. Vor dem Compilen 
jedoch die Flash-Adresse geändert. Ist das soweit korrekt ausgeführt? 
Ich sehe mit ST Link auch bei 0x08004000 den Code vom LED Beispiel.

von Super G. (Gast)


Lesenswert?

Ganz oben in meiner Hauptanwenung, also das erste was in der main() 
drinsteht ist folgende Zeile:
1
/* First 0x20000 bytes reserved for bootloader, eeprom, etc.
2
  application start at 0x08020000 (flash sector 5) */
3
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x20000);

Und außerdem habe ich in der "STM32F407ZE_MemoryMap.xml"
folgende Änderung drin (ganz unten in der Datei):

<MemorySegment size="0x20000" access="ReadOnly" name="FLASH" 
start="0x08020000"/>

Also die Startadresse auf 0x08020000 statt 0x08000000.

Kann man je nach IDE vermutlich auch an anderer Stelle unter den 
Projekt-Parametern eintragen.

Die Funktion "NVIC_SetVectorTable()" ist in der StdPeriph_Driver in der 
"misc.c/h"

Sorry Feierabend, weitere Fragen morgen.

von Franz Sauerdorf (Gast)


Lesenswert?

Danke für die Buchempfehlung!

Ich habe in meinem Hauptprogramm lediglich die Memory Zuordnung 
geändert:
1
MEMORY
2
{
3
  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
4
  CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
5
  FLASH (rx) : ORIGIN = 0x08004000, LENGTH = 1008K
6
  FLASHB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
7
  EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
8
  EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
9
  EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
10
  EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
11
  MEMORY_ARRAY (xrw)  : ORIGIN = 0x20002000, LENGTH = 32
12
  
13
14
}
Damit fängt meine Hauptanwendung erst bei 0x08004000 also Sector 1 an 
und sollte den Bootloader in Ruhe lassen :-) .


Beim Bootloader habe ich nichts verändert. Ist das soweit korrekt?

von Franz Sauerdorf (Gast)


Lesenswert?

So er springt erfolgreich ins Hauptprogramm.
Nur im Hauptprogramm können noch keine Interrupts verwendet werden, 
ansonsten hängt er sich auf. Die Interrupts laden wohl noch im 
Bootloader.
Ich verstehe noch nicht ganz, wie ich den Interrupt-Vector des 
Hauptprogramms mit einem Offset versehe.
Hat da jemand einen Tipp für mich, was ich mir mal ansehen muss?

von Vincent H. (vinci)


Lesenswert?

Bevor du die Interrupts im Hauptprogramm wieder aufdrehst musst du das 
VTOR Register deiner neuen Tabelle zuweisen. Unter Verwendung von CMSIS 
wäre das wie Eingangs eh schon jemand geschrieben hat
1
SCB->VTOR = &"Tabelle"

Ob die Tabelle im Flash oder im RAM steht ist egal. Die Tabelle selbst 
ist ein Mischmasch aus M4-Core Exceptions und Hersteller abhängiger 
Interrupts.

Näheres etwa hier:
http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/DUI0553A_cortex_m4_dgug.pdf


/edit
Pass auf, dass der SysTick nicht zu früh aufgedreht wird. Führ auf 
keinen Fall irgendwelche Initialisierungen in irgendwelchen Bibliotheken 
(etwa HAL) aus, bevor VTOR richtig gesetzt ist!

: Bearbeitet durch User
von Franz Sauerdorf (Gast)


Lesenswert?

Danke die Infos haben mich schon weiter gebracht.

Es kann sein, dass meine Umgebung (Eclipse, GCC) schon etwas 
initialisiert.
Dazu habe ich eine _initialize_hardware.c Datei gefunden, wo die Clocks 
gesetzt werden. Sollte ich den Interrupt Vector nicht schon dort setzen?
Ich habe es zunächst hiermit versucht, was ich direkt oben in die Main 
gesetzt habe:
1
SCB->VTOR = 0x00004000;
Das funktioniert leider nicht. D.h. hatte ich mich auf die Suche nach 
den Initialisierungen davor gemacht.

Ist mein Vector Befehl überhaupt korrekt? Das müsste doch funktionieren?

Vielen Dank.

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


Lesenswert?

... guck Dir doch einfach mal den Beispielcode aus meinem Buch an, der 
tut's. Du musst dazu nix kaufen, einfach nur den Code runterladen (der 
ist frei zugänglich).

von Franz Sauerdorf (Gast)


Lesenswert?

Danke, das Buch habe ich bereits. Ich habe nun erneut reingeschaut, 
jedoch kann ich deinen Code nicht ganz nachvollziehen und habe dazu 
einige Fragen.

Du änderst die IVT bevor du in das Hauptprogramm springst, also im 
Bootloader? Ich dachte man muss dies erst im Hauptprogramm tun (so früh 
wie möglich)?

Dein Code ist folgend dargestellt. Leider ist der für mich schlecht 
lesbar, ich habe ihn versucht an meinen Code anzupassen, jedoch verstehe 
ich nicht, was und wie du dein Hauptprogramm als "g_pfnVectorsFWStubs" 
verwendest. Sieht für mich aus wie eine Funktion mit einem Array? Noch 
nie gesehen.
1
// reprogram the IVT. The Instruction Barrier is necessary here (cf. ARM documentation)
2
        __asm volatile
3
        (
4
            " cpsid i    \n"
5
            " isb        \n"
6
        );
7
        *((unsigned long *)0xe000ed08) = ((unsigned long)(&g_pfnVectorsFWStubs) & (unsigned long)0x1FFFFF80);   // set SCB->VTOR
8
9
        // redirect stack pointer to vector 0 in the application's IVT
10
        __asm volatile 
11
        (
12
            " ldr r0,pcrelSP  \n"
13
            " ldr r13,[r0]    \n"
14
            " .align 2        \n"
15
            "pcrelSP: .word g_pfnVectorsFWStubs \n"
16
         );
17
         // dispatch to app entry
18
        (g_pfnVectorsFWStubs[1])();


Wieso hast du das so realisiert? Die C-Funktion welche auch in den 
Bibliotheken verwendet wird erscheint mir einfacher, folgend dargestellt 
wie ich es versucht habe.
1
#define NVIC_VectTab_RAM             ((uint32_t)0x20000000)
2
#define NVIC_VectTab_FLASH           ((uint32_t)0x08000000)
3
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)
4
{
5
  SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80);
6
}
Und dann der Aufruf, den ich versucht habe:
1
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x4000);

Da hänge ich gerade noch fest.
Vielen Dank für die Unterstützung.

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


Lesenswert?

Hallo Franz,

ich habe bei dem Beispielcode bewusst versucht, Abhängigkeiten zu 
bestimmten Ökosystemen und Bibliotheken zu vermeiden (steht auch 
irgendwo) und den Code mit möglichst vielen Toolchains und Ökosystemen 
übersetzbar zu machen, deswegen keine Bibliotheksfunktionen und auch 
keine selbstgebastelten symbolischen Identifier... ist natürlich für 
realen Code ein Nono, aber Lesbarkeit und Knappheit sind für mich 
vorrangig imperativ gewesen.

Die Philosophie hinter dem Bootloaderbeispiel ist die der Parallelwelten 
- der Bootloader läuft mit seiner eigenen IVT und mit seinem Standalone 
Code, und sobald er sich sicher ist, daß eine gültige Applikation 
geladen ist, wird die Kontrolle an die Applikation übergeben, wobei die 
Applikation genauso gebaut ist als wenn es keinen Bootloader gebe; sie 
(die Applikation) hat also eine eigene IVT, die genau so aufgebaut ist 
wie wenn sie standalone funktionieren würde (also mit gültigem Stack 
Pointer in Vector 0 und gültigem "Reset ISR" in Vektor 1, wobei die 
Applikation dann natürlich auch mit den dort hinterlegten Werten 
hochstartet).

Damit das so realisiert werden kann, müssen zwei dem Bootloader bekannte 
IVT Adressen existieren (das ist recht genau in Kapitel 9 beschrieben; 
sollte es unklar sein, würde ich mich gerne mit Dir Offline über die 
mißverständlichen Stellen auseinandersetzen - natürlich gegen 
Dankeschön! ;-)):

Im Linker Layout ist die Position der "Applikations IVT" bindend 
festgezurrt, so daß der Bootloader immer genau weiß, wo die liegt, 
nämlich in dem Identifier, der mit g_pfnVectorsFWStubs bezeichnet ist. 
Sobald eine gültige Applikation geladen ist, verbirgt sich an der Stelle 
natürlich auch genau die gültige Applikations IVT; es kann aber 
natürlich auch passieren, daß da (noch) nichts steht, z.B. wenn initial 
nur der Bootloader im Flash ist aber noch keine Applikation (in dem Fall 
darf natürlich der Bootloader nicht verzweigen, sondern muß auf die 
Kommunikation eines gültigen Images warten). Deswegen ist der Bezeichner 
mit "stub" (also Platzhalter) gekennzeichnet. Die Deklaration

extern void (* const g_pfnVectorsFWStubs[IVTENTRYCT])(void);


als Array von Funktionspointern ist natürlich in C völlig legitim (Du 
wirst feststellen, daß bei in C geschriebenen Startup files eine IVT in 
der Regel ganz genau so deklariert wird).

Ich biege als "letzte Amtshandlung" des Bootloaders vor Übergabe der 
Kontrolle an die Applikation die IVT um, weil der Bootloader natürlich 
keine Annahmen darüber treffen kann und darf, ab wann die 
Applikationsfirmware Interrupts benutzen will; die IVT sollte sich also 
ab dem ersten Befehl der Applikation so verhalten, wie die Applikation 
es erwartet. Natürlich läßt sich (wie bei Allem) darüber debattieren, ob 
eine andere Implementation besser wäre, aber unter der o.g. Prämisse, 
daß sich der Applikationscode so wenig wie möglich von einer "standalone 
Firmware" unterscheidet (die aus einem physikalischen Reset heraus 
läuft), halte ich die Implementation in meinem Code für konsequenter. 
Wichtig ist natürlich, daß Interrupts definitiv disabled sind UND die 
instruction Barriere gesetzt wird (also die Pipeline geflusht wird), 
bevor die IVT versetzt wird.

Ich benutze g_pfnVectorsFWStubs für alle drei in diesem Zusammenhang 
relevanten Operationen - als Basisadresse für das Umsetzen der IVT, als 
Zieladresse für den SP in Vektor 0 und als Zieladresse für den PC in 
Vektor 1 -, weil es logisch eben genau dieses ist: Die Applikations IVT 
mit diesen Initialvektoren. Würde ich die (in meinem Code aufgelöste) 
SCB->VTOR Adresse mit etwas Anderem als g_pfnVectorsFWStubs belegen, 
dann müßte ich entweder einen Alias für dieselbe Adresse anlegen oder 
aber sicherstellen, daß bei einem Umlegen der IVT auf eine andere 
Adresse (z.B. bei einem zweiten Projekt mit einem anderen Controller) 
die Änderung an beiden Stellen parallel geführt werden müßte, was immer 
eine unnötige Fehlerquelle ist.

Macht das Sinn? Wenn nicht frage gerne weiter (Offline oder Online).

Schönen Dank fürs Buch kaufen; ich hatte gehofft, die Architektur 
verständlich genug im Kapitel 9 dargelegt zu haben...

: Bearbeitet durch User
von Franz Sauerdorf (Gast)


Lesenswert?

Danke für die ausführliche Antwort.
Nunja, das Buch ist verständlich geschrieben, jedoch ist der Code für 
mich dann doch etwas unübersichtlich. Vor allem der der Linker. Ich weiß 
was du vor hast, verstehe jedoch die Notation nicht. Ich denke aber, das 
ist nicht so schlimm. Klar deine Argumentation wieso du es so gemacht 
hast ist vollkommen richtig und gut so, jedoch für meinen Anwendungsfall 
reicht dies abgespeckt. Ich habe vorher noch nie etwas in der Richtung 
angewendet und sammle gerade erst meine Erfahrungen.

Für das Hauptprogramm:
1
/*
2
 * Memory Spaces Definitions.
3
 *
4
 * Need modifying for a specific board. 
5
 *   FLASH.ORIGIN: starting address of flash
6
 *   FLASH.LENGTH: length of flash
7
 *   RAM.ORIGIN: starting address of RAM bank 0
8
 *   RAM.LENGTH: length of RAM bank 0
9
 *
10
 * The values below can be addressed in further linker scripts
11
 * using functions like 'ORIGIN(RAM)' or 'LENGTH(RAM)'.
12
 */
13
14
MEMORY
15
{
16
  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
17
  CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
18
  FLASH (rx) : ORIGIN = 0x08004000, LENGTH = 1008K
19
  FLASHB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
20
  EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
21
  EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
22
  EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
23
  EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
24
  MEMORY_ARRAY (xrw)  : ORIGIN = 0x20002000, LENGTH = 32
25
}
Wie man erkennen kann, fängt der Flash beim Sector 1 an, der Bootloader 
liegt im Sector 0. Das ist dann auch die einzige Veränderung, die ich 
vorgenommen habe. Das Springen vom Bootloader in das Hauptprogramm 
funktioniert auch, wenn das Hauptprogramm keine ISR's enthält.

Ich bin jetzt total verwirrt, ich möchte wie in deinem Buch Abschnitt 
9.5.1 Abbildung 9.3 meinen Programmspeicher aufbauen.
Im Web habe ich wenig darüber gelesen, dass weitere Änderungen in der 
Memory-Definition notwendig sind. Also bisher habe ich gedacht, das 
reicht zur Anpassung des Speicherlayoutes, den Flash nach hinten zu 
schieben und der IVT-Vector rückt dann automatisch mit. Diesen muss man 
man dann im Bootloader vor Programmaufruf noch auf die neue Adresse 
schieben und fertig.

Ist es also weiterhin notwendig das Speicherlayout wie in deinem Buch 
beschrieben anzupassen? Kann ich den IVT Vektor nicht auch so 
verschieben (vermutlich habe ich bisher noch einen kleinen Fehler 
irgendwo)?
Ansonsten würde ich im Bootloader das Memorylayout wie bei dir 
beschrieben übernehmen.
Also ich vermute das ich bisher lediglich die Startadresse der App-IVT 
falsch setze.

Liebe Grüße

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


Lesenswert?

Willst Du mal den map file einstellen? Da sieht man i.d. Regel recht 
schnell wo es hakt...

von Franz Sauerdorf (Gast)


Lesenswert?

Das was ich oben von dem Linkerfile gepostet habe, ist schon alles, was 
ich im Bootloader mache.
Und dann bevor ich zur App springe:
1
        __disable_irq();
2
  NVIC_DisableIRQ(USART2_IRQn);
3
  NVIC_DisableIRQ(TIM2_IRQn)
4
5
6
  uint32_t ApplicationAddress = 0x08004000;
7
8
  SCB->VTOR = (0x08000000 | 0x4000);
9
  __DSB();
10
11
  typedef  void (*pFunction)(void);
12
13
  /* Base address of the Flash sectors */
14
  #define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000)
15
  #define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000)
16
17
  /* Private function pointer */
18
  static pFunction Jump_To_Application;
19
  /* Private variables */
20
  static uint32_t JumpAddress;
21
22
  /* Jump to user application */
23
  JumpAddress = *(__IO uint32_t*) (ADDR_FLASH_SECTOR_1 + 4);
24
  Jump_To_Application = (pFunction)JumpAddress;
25
  /* Initialize user application's Stack Pointer */
26
  __set_MSP(*(__IO uint32_t*) ADDR_FLASH_SECTOR_1);
27
28
        __set_PSP(*(__IO uint32_t*)ApplicationAddress);
29
  Jump_To_Application();

von Mike R. (thesealion)


Lesenswert?

Das Einstellen der Vector Tabelle musst du im Hauptprogramm machen.

Wenn man z.B. die Std. Library Bon ST benutzt gib es eine Init funktion, 
die noch vor Main aufgerufen wird. Und in dieser ist sogar schon die 
passende Zeile für die Tabelle vorhanden ( nur im Fall eines 
Bootloader's halt mit der falschen Adresse ).

Schau dir mal das startup Script deines Hauptprogramms an (meist ein asm 
File) da musste sich der eigentliche Boot Ablauf finden lassen.

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


Lesenswert?

Hallo Franz,

Du must UNBEDINGT(!)

- sämtliche Interrupts disablen (cpsied). Ist das das wohin 
__disable_irq() auflöst?
- eine ISB setzen (DSB ist nicht unbedingt nötig)

bevor Du die Tabelle umsetzt!

Wenn Du einen Debugger hast, der Singlesteppen auf Assemblerebene 
erlaubt, steppe bis zum Aufruf von Jump_To_Application(), sieh Dir die 
Register an der Stelle an, wo der bl Aufruf steht (speziell die stack 
pointer), dann mach einen step into und sieh wo Du landest. Wirklich am 
Einsprung der Applikation, oder direkt am Fault Handler? Was genau steht 
zu dem Zeitpunkt an der Adresse 0x8004000 und 0x8004004? Hast Du da 
vielleicht ein little endian/big endian Problem?

Soweit sieht es für mich ok aus; das einzige, woran es vielleicht hakeln 
könnte ist daß die Sprungadresse mit dem LSB gesetzt (also ungerade) 
erwartet wird (Thumb convention).

BTW, in deinem Codefragment benutzt Du drei verschiedene Wege, dieselbe 
Adresse zu referenzieren (ApplicationAddress,ADDR_FLASH_SECTOR_1 und 
(0x08000000 | 0x4000)). Das ist enorm fehleranfällige Codierung, weil Du 
dann bei jeder Änderung drei Stellen umschreiben musst, ausserdem ist es 
nicht gut lesbar... aus genau dem Grunde gehe ich über 
g_pfnVectorsFWStubs, der muss im Falle genau an einer Stelle geändert 
werden.

von Franz Sauerdorf (Gast)


Lesenswert?

Danke an Mike und Ruediger für eure Antworten.

@Mike: Ich überprüfe das die Tage.

@Ruediger: Ich habe im Bootloader lediglich für den UART2 und für den 
Timer2 einen Interrupt verwendet, welche ich im NVIC->ICER wieder 
deaktiviere. Weiterhin deaktiviere ich Global alle Interrupts 
(CPSR/cpsid) mit __disable_irq().

ISB fehlte danke, die habe ich nun hinter die DSB getan.

Leider habe ich das "neue" Board vom Discovery mit dem Flash Laufwerk in 
Windows und anderem Treiber. Mit diesem funktioniert OpenOCD nicht mehr 
korrekt. Welchen Debugger benutzt du/ihr?

Zum BTW: Danke, das sieht bis zum Funktionstest oft bei mir so aus :-/

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


Lesenswert?

soweit ich weiss, haben alle discovery boards einen On-Board STLink, d.h 
Du kannst alle Debugger verwenden, die mit einem ST Link kommunizieren 
können. Ich benutze für das 407 discovery board WinIdeaOpen ohne 
jegliche Probleme. Btw, Du kannst sogar den On Board ST Link durch 
umflashen zum Segger J-Link machen und Dir damit zusätzlich die J-Link 
Vorteile für umme sichern.

Wo "nur" eine JTAG/SWD Schnittstelle zur Verfügung steht, habe ich einen 
iTAG50 fertig montiert und geflasht gekauft, Du kannst Dir den aber auch 
selber basteln.

Wo genau hängt es sich denn bei Dir auf? Wie gesagt, wenn Du direkt vor 
dem bl den Prozessorstatus inspizieren kannst, sollte relativ schnell 
klar werden, wo das Problem ist...

von Franz Sauerdorf (Gast)


Lesenswert?

@Mike: Ja die stdlib wird bei mir verwendet. Ich finde jedoch nur die 
Header-Datei und keinerlei Code zur Initialisierung. Wo hast du das mal 
gesehen? Ich habe noch eine _initialize_hardware.c wo jedoch eigentlich 
nur die Takte gesetzt werden.

Ja das haben sie.
Er startet das Debugging und hält bei dem Einstieg der Main an, wenn ich 
ihn dann rennen lasse, bricht die JTAG Verbindung ab. Achja und jedes 
mal vorher muss ich manuell den Chip erasen :-/ .
Bei einem Freund mit dem älteren ST-Link drauf funktioniert das.

Die Fehlermeldung ist:
Error: JTAG failure -4
Error: jtag status contains invalid mode value - communication failure
Polling target stm32f4x.cpu failed, trying to reexamine
Examination failed, GDB will be halted. Polling again in 100ms

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


Lesenswert?

Also Du meinst mit der main() die main() des Bootloaders, in dem dann 
die Magie mit dem Umsetzen vorgenommen wird? Oder die main() der 
Applikation?

Vermutlich das Erstere. Was passiert, wenn Du versuchst single zu 
steppen? Verabschiedet sich die Kommunikation zur Probe dann ebenfalls?

So wie Du es schilderst, ist es ein Problem im Zusammenspiel zwischen 
Debugger und Probe. Lad Dir doch einfach mal WinIdeaOpen herunter, das 
spielt mit dem EVB (und meiner Beispielapp) aus der Box heraus. 
Herauszufinden, warum eine besteimmte Combo Debugger/Probe es nicht tut, 
ist eine Menge Zeit für Nichts zu verschwenden.

BTW, was genau heisst "Bei einem Freund mit dem älteren ST-Link drauf 
funktioniert das?" WAS funktioniert? Singlesteppen? Kannst Du deinen 
Code mit dem STLink deines Kumpels debuggen?

von Franz Sauerdorf (Gast)


Lesenswert?

Ja genau. Die Main des Bootloaders. Singlesteppen funktioniert leider 
auch nicht. Ich habe mir WinIdeaOpen heruntergeladen und es funktioniert 
auf Anhieb Beispielprojekte zu debuggen. Dein Projekt irgendwie nicht so 
richtig. Sobald ich aber unter Debug/Files die LED App von dir 
aktiviere, blinkt er gar nicht mehr. Vorher wenigstens einmal in der 
Sekunde. Er bleibt mit dem Hauptprogramm von dir in Adresse 0 stehen. 
Ist ja auch nicht so wichtig, dass dein Projekt funktioniert.

Ich kann morgen von meinem Kumpel das Board probieren. Ich habe gelesen, 
dass es eine Änderung gab. ST-Link V2-1 und das Board von meinem Kumpel 
wird auch nicht als Massenspeicher unter Windows erkannt, meins schon. 
Das ist ein Feature und gewollt. Jedoch macht OpenOCD da irgendwie nicht 
mit. Darüber habe ich leider sehr wenig im Internet gefunden. Die JTAG 
Fehlermeldung hatten auch einige bei zu geringer Versorgungsspannung. 
Mit externer Spannung habe ich es ebenfalls probiert, hat nicht zum 
Erfolg geführt.

Kann man ein Discovery mit einem anderen Debuggen (weil ich bekomme den 
fest verbauten STLink nicht vollständig vom Chip getrennt)? BTW: ich 
habe sogar noch einen einzelnen ST Link V2 da... ich such mir mal die 
Pins raus und probiere es mal.

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


Lesenswert?

ja, Du musst ja auch beide Images laden - den Bootloader UND die 
Applikation. Dafür gibt es die Target Download Option in WinIdea.

von Franz Sauerdorf (Gast)


Lesenswert?

Ich habe wie in der readme zunächst das Hauptprogramm gebaut, dann den 
Bootloader. Anschließend im Bootloader den Haken vom Hauptprogramm 
entfernt und wieder hinzugefügt. Wie gesagt, ohne HP blinkt es, mit 
nicht.

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


Lesenswert?

aha, also funktioniert es prinzipiell. Das Blinken ist ein Indikator, 
dass der Bootloader kein gültiges Applikationsimage gefunden hat. Wenn 
die Booloader LED NICHT blinkt, heisst es entweder, dass sich die 
Geschichte aufgehängt hat und gar nichts mehr geht - oder eben das 
Applikationsprogramm läuft aber nichts sichtbares tut. Mein 
Applikationsprogramm blinkt andere LEDs; wenn es das bei Dir nicht 
tut, kann es sein daß Du ein Anderes Eval Board mit Anderen Peripherien 
hast, so dass man schlicht und einfach nicht sieht, was es tut?

Aber mit WinIdea Debuggern funktioniert? Kannst Du im Bootloader bis zur 
Übergabe an das Hauptptogramm durchsteppen?

von Franz Sauerdorf (Gast)


Lesenswert?

Also das Debugging funktioniert nun. Ich musste an mehreren Stellen 
Anpassungen vornehmen, was es wie ein Zufall aussehen lässt, wenn man 
dann doch mal die richtigen Anpassungen zusammen hat. Ich musste das 
Script vom OpenOCD anpassen, mit den Windows Treibern kämpfen usw.

Nun habe ich auch ziemlich sicher herausgefunden, wieso das 
Hauptprogramm mit ISR nie funktioniert hat. Die Standardbibliotheken 
setzen den Takt und anscheinend noch einiges mehr. Mir ist aufgefallen, 
dass obwohl ich im Bootloader die globalen Interrupts ausschalte, sie im 
HP wieder aktiv waren. Dann habe ich ein Programm erstellt, wo ISRs im 
HP funktionierten, ich wusste zunächst nicht wieso. Ich habe dann 
stückweise Teile vom Bootloader auskommentiert, es hat funktioniert bis 
nur noch folgendes übrig blieb (im HP habe ich keine Anpassungen 
gemacht, außer in der Memory-Linker-File die Flash-Adresse):
1
  typedef  void (*pFunction)(void);
2
3
  /* Base address of the Flash sectors */
4
  #define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000)
5
  #define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000)
6
  #define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000)
7
  #define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000)
8
  #define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000)
9
  #define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000)
10
  #define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000)
11
  #define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000)
12
  #define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000)
13
  #define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000)
14
  #define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000)
15
  #define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000)
16
17
  /* Private function pointer */
18
  static pFunction Jump_To_Application;
19
  /* Private variables */
20
  static uint32_t JumpAddress;
21
22
  /* Jump to user application */
23
  JumpAddress = *(__IO uint32_t*) (ADDR_FLASH_SECTOR_1 + 4);
24
  Jump_To_Application = (pFunction)JumpAddress;
25
  /* Initialize user application's Stack Pointer */
26
  //__set_MSP(*(__IO uint32_t*) ADDR_FLASH_SECTOR_1);
27
  //__set_PSP(*(__IO uint32_t*) ADDR_FLASH_SECTOR_1);
28
29
        Jump_To_Application();

Ich musste nicht einmal den Stackpointer umsetzen. Ich habe dann mehrere 
Anläufe gebraucht um herauszufinden wieso das nur mit dem einen Projekt 
funktioniert hat. Ich habe die gleichen Interrupts wie im Bootloader 
verwendet z.B. Timer2, aber andere LEDs im ISR blinken lassen, somit 
wusste ich, dass er wirklich meine HP ISRs auch ausführt.
Jedenfalls ist mir aufgefallen, dass wenn ich die Memory-Linker-File 
bearbeite, er selbst nach einem clean und rebuilt die hex nicht auf die 
neue Flashadresse korrigiert. Ich musste den gesamten "debug" bzw. 
"release" Ordner löschen, dann hat er auch die Flash-Adresse übernommen. 
Anscheinend initialisiert er jedoch woanders die anderen Adressen, die 
ich nicht mehr ändern könnte.
Ich konnte es jetzt lösen indem ich ein neues Projekt erstelle und vor 
dem ersten Build direkt das Memory-File anpasse, also lediglich die 
Flash-Adresse ändere. Er muss bei der Initialisierung die Adresse z.B. 
für den ISR-Vector und Stack überschreiben, weshalb es nie funktioniert 
hat.

Ich würde nun gerne herausfinden, wo er die Adresse im Projekt 
speichert, sodass ich zunächst weiß was er genau macht und dies auch 
manuell noch einmal anpassen kann.

Hat jemand dazu eine Idee?

von Franz Sauerdorf (Gast)


Lesenswert?

Dazu hatte Mike schon etwas geschrieben, ich bin die stdlib usw. 
durchgegangen, konnte jedoch nur den Teil finden, wo er den Takt setzt. 
Den Teil wo er die Adressen setzt, konnte ich bisher nicht finden.

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


Lesenswert?

Glückwunsch erstmal, dass Du Dich durch die Probleme mit den tools 
durchgebissen hast!

Worauf zeigt denn der jetzt 0x8004004? Was genau macht die Funktion, die 
dahinter steckt? Nicht zufällig irgendwas, das über Dir nicht 
kontrollierbare Bibliotheksfuntionen den Prozessor reinitialisiert? Dann 
kannst Du Dir natürlich die ganze Aktion mehr oder weniger sparen.

Mit dem SP: Das habe ich ja genauer in meinem Buch ausgeführt; wenn Du 
sicherstellen kannst, dass der vom Bootloader vorbereitete Stack auch 
für die Applikation ausreicht, kannst Du natürlich auf dem selben Stack 
weiterarbeiten. Aber davon rate ich ganz stark ab, weil ja eine spätere 
Version der Applikation den Stack völlig Anders benutzen kann, die müßte 
dann den SP direkt nach Übergabe der Kontrolle selber umsetzen.

von Franz Sauerdorf (Gast)


Lesenswert?

Wie meinst du das, worauf die zeigt? Ab 0x08004000 beginnt mein HP was 
momentan lediglich den Timer, UART und den SysTick verwendet, alles was 
auch der Bootloader benutzt (zur Kontrolle verwende ich diese 
ebenfalls).

Ich habe gelesen, dass man zur HP-Adresse +4 springen muss, weil man 
sonst zur initial Stack Pointer Position springen würde. Man würde also 
zur Basisstackadresse springen, wenn ich das richtig verstanden habe.
Ab +4 beginnen dann die Adressen der reset handler procedure. So ganz 
geläufig ist mir das noch nicht.

Ich meine mit dem SP, dass ich glaube im HP initialisiert einer der 
Standardbibs den Stack eh auf 0x08004000, somit ist es egal was ich im 
Bootloader mache. Ich müsste die Stelle finden, wo es im HP bei der 
Initialisierung gemacht wird.

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.