Hi,
ich möchte gerne einen einfachen Bootloader für mein STM32F4Discovery
Board schreiben, natürlich nur zum Lernzweck. Ich nutze die Embedded ARM
GNU Toolchain und CoIDE. Zunächst wollte ich einfach nur eine Funktion
in den SRAM kopieren und dann dort hin springen, was mir leider nicht
gelingt. Ich komme nicht weiter, ich habe Google befragt und in den
Datenblättern nachgesehen, finde allerdings den Fehler nicht. Immer wenn
er zur Funktion springt, endet es damit, dass eine unbehandelte
Exception auslöst und im default Handler hängen bleibt.
main.c:
1
#include"stm32f4xx.h"
2
3
#define APPLICATION_ADDRESS (void*)0x20000000
4
5
voidmemcpy(void*dst,void*src,uint32_tlen)
6
{
7
uint8_t*dest_ptr=dst,*src_ptr=src;
8
uint32_ti;
9
for(i=0;i<len;i++)
10
{
11
*dest_ptr=*src_ptr;
12
dest_ptr++;
13
src_ptr++;
14
}
15
}
16
17
voiddoSomething(void)
18
{
19
while(1)
20
{}
21
}
22
23
intmain(void)
24
{
25
SystemInit();
26
27
//enableLED();
28
memcpy(APPLICATION_ADDRESS,&doSomething,4*32);
29
30
void(*func)(void)=APPLICATION_ADDRESS+1;
31
func();
32
33
while(1)
34
{
35
}
36
}
Die Vektor Tabelle muss ich erst in den SRAM verschieben, wenn ich sie
nachträglich bearbeiten will, also einen Handler auch im SRAM haben
will, oder?
Ich weiß auch, dass ich wahrscheinlich in den User Stack schreibe,
allerdings am Ende und der Stack dürfte groß genug sein. Wo finde ich
den die Einstellung dazu? In der CoIDE finde ich dazu nichts, muss
wahrscheinlich dem Linker das passende Argument liefern.
Auch ist dieses 4*32 unschön, gibt es eine Möglichkeit im GCC die Größe
einer Funktion herauszubekommen ohne den Assemblercode ausgeben zu
lassen und die Maschinenbefehle zu zählen?
Christopher C. schrieb:> #define APPLICATION_ADDRESS (void*)0x20000000
Du möchtest also die automatische RAM-Verwaltung vom Linker nicht und
stattdessen alle Adressen selber verwalten? Wenn du so etwas machst
darfst du keinerlei "static" und globale Variablen mehr verwenden!
Meines Wissens verwendet die StdPeriphal lib bzw. die Clock Templates
aber mindestens schon 1 globale Variable, SystemCoreClock. Die müsstest
du rausnehmen.
Einfacher ist es natürlich ein Array zu reservieren und dort
hineinzukopieren:
1
charapplicationCode[4*32];
Christopher C. schrieb:> Die Vektor Tabelle muss ich erst in den SRAM verschieben, wenn ich sie> nachträglich bearbeiten will, also einen Handler auch im SRAM haben> will, oder?
Du kannst die Tabelle mithilfe des VTOR Registers in den RAM
verschieben, wenn du die Inhalte zur Laufzeit ändern willst. Ob die
auszuführenden Handler im RAM sind oder nicht hat nichts damit zu tun
ob die Tabelle im RAM ist oder nicht.
Christopher C. schrieb:> Ich weiß auch, dass ich wahrscheinlich in den User Stack schreibe,> allerdings am Ende und der Stack dürfte groß genug sein.
Ich sehe nicht wo du dein Stack 128kB verbraucht...
Christopher C. schrieb:> Wo finde ich> den die Einstellung dazu?
Im Linkerscript (*.ld). Ist "etwas" kryptisch.
Christopher C. schrieb:> Auch ist dieses 4*32 unschön, gibt es eine Möglichkeit im GCC die Größe> einer Funktion herauszubekommen
Das müsstest du über den Linker machen, indem du die Funktion in eine
eigene "Spezial"-Sektion packst, vor und nach die Funktion ein label
setzt im Linkerscript und im Code die Differenz errechnest.
Christopher C. schrieb:> Immer wenn> er zur Funktion springt, endet es damit, dass eine unbehandelte> Exception auslöst und im default Handler hängen bleibt.
Schau dir mal die Disassembly vom Sprung an, wo er genau hinspringt, und
ob das "Ziel" "richtig" aussieht.
Christopher C. schrieb:> memcpy(APPLICATION_ADDRESS, &doSomething, 4*32);>> void (*func)(void) = APPLICATION_ADDRESS +1;
PS: Vermutlich liefert &doSomething bereits eine Adresse zurück, deren
niedrigstes Bit gesetzt ist, um den Thumb-Modus anzudeuten. Versuch mal:
Tatsächlich es funktioniert! Vielen Dank.
Ich frage mich nur wie diese Rechnung zu stande kommt:
Dr. Sommer schrieb:> memcpy(applicationCode, (char*) (((uintptr_t) (&doSomething)) &> (~((uintptr_t) 3))), 4*32);
Ich hab mal im Debugger nachgeschaut, doSomething liegt bei 0x8000244.
Deine Rechnung ergibt: 0x8000240 also einen Maschinenbefehl zuvor.
Warum?
Dr. Sommer schrieb:> Christopher C. schrieb:>> #define APPLICATION_ADDRESS (void*)0x20000000> Du möchtest also die automatische RAM-Verwaltung vom Linker nicht und> stattdessen alle Adressen selber verwalten? Wenn du so etwas machst> darfst du keinerlei "static" und globale Variablen mehr verwenden!> Meines Wissens verwendet die StdPeriphal lib bzw. die Clock Templates> aber mindestens schon 1 globale Variable, SystemCoreClock. Die müsstest> du rausnehmen.
Das Problem ergibt sich, da der Linker hier relative Adressen angibt,
oder?
Wenn ich eine Hex Datei in den SRAM kopiere, dürfte sich das Problem
eigentlich erledigen, da hier die Adressierung wieder stimmt solange das
User Programm auf seine eigenen globalen Variablen zugreift. Allerdings
muss ich beim User Programm den Linker mit einem Linkerscript richtig
einstellen, damit sich keine Speicherbereiche überschneiden.
Habe ich das richtig verstanden?
Nein halt, habe mich verechnet:
Die Rechnung ergibt doch: 0x800244. Wie kann das sein das es da doch
geht, es kommt doch das gleiche raus. Es muss also am Thumb-Bit liegen.
Deine Rechnung nimmt es raus, oder? Der GCC setzt in Wirklichkeit das
Thumb-Bit.
Christopher C. schrieb:> doSomething liegt bei 0x8000244.
Ja. Aber wenn du &doSomething eingibst wird der Compiler vermutlich
0x8000245 als Funktionspointer verwenden, um, wie gesagt, dem
Sprung-Befehl "BX" bzw. "BLX" mitzuteilen, dass die Funktion den
Thumb-Instruction Set verwendet (schau dir im Debugger das "src"
Argument an). Zum umkopieren der Daten musst du aber natürlich die
"richtige" Adresse verwenden.
> Deine Rechnung ergibt: 0x8000240 also einen Maschinenbefehl zuvor.> Warum?
Das kann nicht, das sollte die 0x8000245 zurück auf 0x8000244 setzen.
Die Rechnung ist lediglich ein typesafes auf-0-setzen der unteren 3
bits.
Christopher C. schrieb:> Das Problem ergibt sich, da der Linker hier relative Adressen angibt,> oder?
Nein. Das Problem ist, dass du einfach fröhlich in den RAM ab Adresse
0x20000000 schreibst, ohne dir darum Sorgen zu machen, was dort liegt.
zB könnte ich mir vorstellen dass der Linker die globale Variable
SystemCoreClock genau nach 0x20000000-0x20000003 packt.
> Wenn ich eine Hex Datei in den SRAM kopiere, dürfte sich das Problem> eigentlich erledigen, da hier die Adressierung wieder stimmt solange das> User Programm auf seine eigenen globalen Variablen zugreift.
Nein, du würdest immer noch deine globalen Variablen überschreiben.
> Allerdings> muss ich beim User Programm den Linker mit einem Linkerscript richtig> einstellen, damit sich keine Speicherbereiche überschneiden.> Habe ich das richtig verstanden?
Wenn du das User Programm mit -fPIC kompilierst müsste der User code
komplett unabhängig davon sein, wo er letztendlich landet, d.h. du
kannst ihn irgendwohin - zB in das "applicationCode " Array - kopieren
und von dort ausführen. Im Linkerscript des User Codes musst du
natürlich so oder so dafür sorgen dass Globale Variablen und Code
einfach hintereinander "flach" in die Ausgabedatei gepackt werden, da ja
keine Unterscheidung RAM/ROM mehr nötig ist.
Ich kann leider kaum ARM Assembler und finde deshalb auch nicht die
Stelle wo er die Adresse von doSomething lädt.
Eine kleine Frage am Rande, der Cortex M4 kann doch auch normale ARM
Befehle ausführen und der Thumb Modus ist automatisch drin, oder?
Christopher C. schrieb:> 0800025a: movw r1, #581 ; 0x245> 0800025e: movt r1, #2048 ; 0x800
^-- Dort wird die Adresse 0x0800245 geladen und an memcpy übergeben.
Christopher C. schrieb:> 0800026a: mov.w r3, #536870912 ; 0x20000000> 0800026e: str r3, [r7, #4]> 08000270: ldr r3, [r7, #4]> 08000272: blx r3
Schön ohne Optimierungen kompiliert... Hier wird direkt an die Adresse
0x20000000 gesprungen. Was natürlich falsch ist, das unterste Bit muss
1 sein, denn...
Christopher C. schrieb:> Eine kleine Frage am Rande, der Cortex M4 kann doch auch normale ARM> Befehle ausführen und der Thumb Modus ist automatisch drin, oder?
... der Cortex M4 kann keineARM Instruktionen ausführen, nur Thumb2.
Daher gibt es auch eine Exception, wenn du versuchst ARM-Code
auszuführen durch einen Sprung mit "B(L)X" an eine Adresse deren
unterstes Bit 0 ist.
Christopher C. schrieb:> Ich kann leider kaum ARM Assembler und finde deshalb auch nicht die> Stelle wo er die Adresse von doSomething lädt.
Das ist schlecht, wenn man derart hardwarenah programmieren will... Das
ist doch die Gelegenheit es zu lernen.