Forum: Compiler & IDEs Parameterübergabe bei Inline Assembler


von Oliver B. (irq)


Lesenswert?

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
asm volatile ("mov R0, 0x3000");  /* lower bytes  */
2
asm volatile ("movt R0, 0x0800"); /* higher bytes */
3
asm volatile ("ldr SP, [R0]");

Nur beim Übergeben des Wertes (uint32_t) 0x08003000 per Parameter mache 
ich wohl irgendwas falsch:
1
void setSP(uint32_t mySP)
2
{
3
  asm volatile ("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.

von Andreas B. (Gast)


Lesenswert?

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).

von (prx) A. K. (prx)


Lesenswert?

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
void setSP(uint32_t mySP)
2
{
3
  asm volatile ("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.

von Oliver B. (irq)


Lesenswert?

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
const uint16_t low = 0x3000;
2
const uint16_t high = 0x0800;
3
4
// Basisadresse der Vektortabelle in R0 laden
5
asm volatile ("mov R0,%0" : : "r" (low));
6
asm volatile ("movt R0,%0" : : "r" (high));
7
8
// SP laden
9
asm volatile ("ldr SP, [R0]");
10
11
// PC laden
12
asm volatile ("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.

von Oliver B. (irq)


Lesenswert?

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
const uint16_t low = 0x3000;
3
asm volatile ("mov R0,%0" : : "r" (low));
4
5
// Das Sorgenkind high bytes fix
6
asm volatile ("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_t base = 0x08003000;
2
asm volatile ("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.

von (prx) A. K. (prx)


Lesenswert?

Den Aufwand, irgendwelches Zeugs in normale Register zu laden, den 
kannst du getrost dem Compiler überlassen.
1
asm volatile ("ldr sp,[%0]\n\tldr pc,[%0,#4]" : : "r"(0x08003000));

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Zum Start der Anwendung aus einem Bootloader müsste man sich nicht 
selbst mit inline-Assembler quälen, denn im CMSIS-Code gibt es etwas 
'Vorgekautes'.
1
typedef void (*FunctionPtr)(void);
2
3
/* User-applications's start-address in flash-memory, see linker script */
4
extern uint32_t _suserapplication;
5
6
static void start_application(void)
7
{
8
  uint32_t tmp;
9
  FunctionPtr pfunc;
10
  uint32_t app_org;
11
  
12
  app_org = (uint32_t)(&_suserapplication);
13
  tmp = *((volatile uint32_t*)(app_org + 4)); // RST-handler
14
  pfunc = (FunctionPtr)tmp;
15
  // opt.: NVIC_SetVectorTable(...
16
  tmp = *((volatile uint32_t*)(app_org + 0)); // initial stack addr.
17
  __set_MSP( tmp ); // see core_cm3.h/.c
18
  pfunc();
19
}

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.