Hallo,
ich programmiere einen STM32 Cortex-M3 Controller mit GCC und würde
gerne einen 32Bit-Wert in das Stackpointer-Register schreiben. Leider
scheitere ich an der Umsetzung des Inline Assembler.
Wenn ich die Adresse hart mit reinkodiere klappt es:
1
asmvolatile("mov R0, 0x3000");/* lower bytes */
2
asmvolatile("movt R0, 0x0800");/* higher bytes */
3
asmvolatile("ldr SP, [R0]");
Nur beim Übergeben des Wertes (uint32_t) 0x08003000 per Parameter mache
ich wohl irgendwas falsch:
1
voidsetSP(uint32_tmySP)
2
{
3
asmvolatile("ldr SP, %0"::"m"(mySP));
4
}
Ich muss zugeben ich habe Null Erfahrung mit Assembler und die
GCC-Inline-Tutorials haben mir nicht weitergeholfen. Hat jemand einen
Tipp?
Viele Grüße, Oliver.
Oliver Behr schrieb:> Leider scheitere ich an der Umsetzung des Inline Assembler.
Was scheitert denn genau?
Ein paar Sachen kann ich mir schon denken. Der Compiler verwendet den
Stack um lokale Variablen, Rücksprungadressen, gespeicherte Register und
so weiter zu speichern. Tauschst du ihm den unter den Füßen gegen einen
anderen, kann das nur schief gehen. Ist im zitierten Fall setSP() die
Optimierung ein, kommt der Compiler ohne eine Stack Frame aus und kann
auf dem ARM auch den Rücksprung ohne Stack durchführen, aber spätestens
die aufrufende Funktion fällt dann böse auf die Nase.
Den SP zu ändern geht nur mit Vorsicht und wenn der vorherige Wert vor
dem Rücksprung in die aufrufende Funktion wiederhergestellt wird. Also
effektiv nur, um andere Funktionen mit einem anderen Stack aufzurufen
oder um einen kompletten Context Switch zu machen, bei dem alle Register
durch einen anderen Satz getauscht werden (Coroutinen oder
Multitasking).
Oliver Behr schrieb:
Du lädst in deinem Code nicht den Wert 0x08003000 in SP, sondern das
Wort an der Adresse 0x08003000.
Wenn du den Wert des Parameters in SP laden willst:
1
voidsetSP(uint32_tmySP)
2
{
3
asmvolatile("mov sp,%0"::"r"(mySP));
4
}
Allerdings kommt es nicht grad oft vor, das solch ein Code in C Sinn
ergibt, i.d.R. ist das nur ein Sprungbrett ins Nirvana.
Vielen Dank für die ausführlichen Antworten. Wenn ich mir es recht
überlege ist es keine geschickte Idee, wenn ich den SP in einer Funktion
setze -> so wirklich weiß ich eigentlich gar nicht, was falsch läuft.
Zur Erklärung: Ich schreibe an einem Bootloader und will mit dem Code
erreichen, dass er zur Anwendung springt. Die Idee ist, dass ich den
initialen SP-Wert lade (also Ende des RAM) und dann in den Program
Counter die Adresse des Reset-Handlers der Anwendung.
Beide stehen in der Vektortabelle der Applikation ganz am Anfang (erstes
und zweites Wort) und die widerum an der Adresse 0x08003000. Insofern
wäre es eigentlich schon richtig, den Wert an der Adresse 0x08...
hineinzuladen und nicht wie ich geschrieben habe, den Wert 0x08... an
sich. War für mich nur erstmal der erste Schritt. Den Wert an der
Adresse könnte ich mir auch mit C holen.
Wenn speziell der SP solche Probleme macht, dann wäre mein Ansatz jetzt,
früher schon den C-Input zu liefern, nämlich bei den MOVs. Allerdings
bekomme ich einen Compilerfehler ("Error: constant expression expected
-- `movt R0,r3'") bei folgendem Code:
1
constuint16_tlow=0x3000;
2
constuint16_thigh=0x0800;
3
4
// Basisadresse der Vektortabelle in R0 laden
5
asmvolatile("mov R0,%0"::"r"(low));
6
asmvolatile("movt R0,%0"::"r"(high));
7
8
// SP laden
9
asmvolatile("ldr SP, [R0]");
10
11
// PC laden
12
asmvolatile("ldr PC, [R0, #4]");
Ich habe versucht, die beiden Variablen low und high global wie lokal zu
definieren, mit verschiedenen Modifiern, aber geholfen hat es nichts.
Wenn ihr einen anderen Vorschlag habt, wie ich den Sprung in die
Applikation bewerkstelligen könnte, bin ich natürlich dafür offen.
Prinzipiell funktioniert "meine" Methode jedoch, allerdings nur, wenn
ich (siehe erstes Posting), die Basisadresse direkt in die MOVs mit
reinschreibe.
Viele Grüße, Oliver.
Ok, ich habe nun eine Lösung gefunden. Nachdem nun immer das movt das
Problem war, habe ich es schrittweise versucht:
1
// Die low bytes über eine Variable
2
constuint16_tlow=0x3000;
3
asmvolatile("mov R0,%0"::"r"(low));
4
5
// Das Sorgenkind high bytes fix
6
asmvolatile("movt R0, 0x0800");
Ergebnis: Hat funktioniert, das wäre zumindest schon mal ein erster
Schritt, d.h. die Parametrierung funktioniert.
Dann ist mir die Idee gekommen: "Warum zwei einzelne 16-Bit-Werte laden,
wenn es auch eine Operation für einen 32-Bit-Wert gibt (->LDR)?"
Herausgekommen ist folgendes Konstrukt:
1
uint32_tbase=0x08003000;
2
asmvolatile("ldr R0,%0"::"m"(base));
Siehe da, es funktioniert. Ehrlich gesagt aber kein Plan warum jetzt "m"
und nicht "r" richtig ist und wieso überhaupt. Falls jemand eine
Erklärung hat, wäre es also interessant sie zu erfahren.
Viele Grüße, Oliver.