Forum: Mikrocontroller und Digitale Elektronik Cortex-M4 SVCall Wechsel auf PSP


von David M. (david107)


Lesenswert?

Hi zusammen,

ich versuche im Moment, in Anlehnung an FreeRTOS, einen preemptiven 
Scheduler zu erstellen.
Das generelle Vorgehen ist mir klar, nur leider komme ich immer in den 
Default Handler anstatt meine Funktion.

Hier mein Code:
1
#include "stm32f4xx.h"
2
3
volatile unsigned long * StackPointer;
4
5
void Task1()
6
{
7
  static int i = 0;
8
  i++;
9
}
10
11
int main(void)
12
{
13
  unsigned long Stack[128];
14
  int i;
15
  for(i = 0; i<=127; i++)
16
  {
17
    Stack[i] = 2;
18
  }
19
    StackPointer = &Stack[127];
20
    /* Set xPSR to an initial value */
21
    *StackPointer = (unsigned long)0x01000000L;
22
    StackPointer--;
23
    /* Set PC */
24
    *StackPointer = (unsigned long)&Task1;
25
    StackPointer--;
26
    /* Set LR */
27
    *StackPointer = 0;
28
    /* Space for r12,r3,r2,r1,r0 */
29
    StackPointer -= 6;
30
    /* Set exec return to an initial value, return to thread mode from PSP, non-floating-point */
31
    *StackPointer = (unsigned long)0xFFFFFFFDL;
32
    /* Space for r11-r4 */
33
    StackPointer -= 8;
34
35
  __asm volatile(
36
    " ldr r0, =0xE000ED08   \n" /* Use the NVIC offset register to locate the stack. */
37
    " ldr r0, [r0]   \n"
38
    " ldr r0, [r0]   \n"
39
    " msr msp, r0  \n" /* Set the msp back to the start of the stack. */
40
    " cpsie i  \n" /* Globally enable interrupts. */
41
    " svc 0    \n" /* System call to start first task. */
42
    " nop    \n"
43
  );
44
45
    while(1)
46
    {
47
    }
48
}
49
50
void SVC_Handler(void)
51
{
52
  __asm volatile ("LDR r0, =StackPointer\n");
53
  __asm volatile ("LDR r0, [r0]\n" );
54
  __asm volatile ("LDMIA r0!, {r4-r11, LR}\n");
55
  __asm volatile ("MSR PSP, r0\n");
56
  __asm volatile ("ORR lr, lr, #4\n");
57
  __asm volatile ("BX lr\n");
58
}

Zum Code:
In dieser Implementierung versuche ich erstmal nur einen Stack irgendwo 
aufzusetzen und auf diesen umzuschalten, also den ersten Prozess zu 
starten.
In den SVCall gelange ich, die Register R4-R11 werden richtig gepopt und 
der LR wird auf 0xFFFFFFFDL gesetzt um vom PSP aus zu starten.
Nun meine Frage: Werden die Register R0-R3,R12 und PC nach dem "BX LR" 
nicht wieder hergestellt? Dies kann ich nicht beobachten.

Ich hoffe mir kann jemand helfen.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Wenn man das deklariert:
volatile unsigned long * StackPointer;

Ist es wohl die aller erste Variable im RAM, wohl bei der Adresse 
0x20000000.

Nun ein:
StackPointer--

dann ist man bei der Adresse 0x1FFFFFFC

>> Hard Fault Exception.

von David M. (david107)


Lesenswert?

Hi Markus,

mit StackPointer = &Stack[127];
setze ich den StackPointer zuvor auf einen "gültigen" Bereich
(liegt dann bei 0x200010B0).
Das Setzen der einzelnen Werte in diesen Stack funktioniert und ich kann 
es im Debugger Schritt für Schritt nachprüfen. Wie gesagt das popen der 
Register aus diesem Stack funktioniert ebenfalls. Erst nach BX LR kommt 
es zum Hard Fault.

Btw. die For-Schleife habe ich nur eingebaut um zu prüfen das beim popen 
die Register wirklich auf einen anderen Wert gesetzt werden.

von Fritz (Gast)


Lesenswert?

Hallo,
Ich programmiere auch gerade einiges im Assembler für den M4.

Der Befehlt BX LR ist ein indirekter jump.
in LR muß dann eine gültige Adresse + 1 (Beim thumb2-Befehlssatz 
notwendig) stehen.
Foglich wäre dein Wert 0xFFFFFFFD ja OK, aber laut Datenblatt vom STM 
ist dort (0xfffffffc) weder RAM noch FLASH!
Also erfolgt zurecht die exception.

von David M. (david107)


Lesenswert?

Hallo Fritz,

dieser Code-Snippet ist eine Kopie von FreeRTOS, da scheint es auch zu 
klappen.

Im Cortex-M4 Generic User Guide steht zudem:
EXC_RETURN = 0xFFFFFFFD
"Return to Thread mode, exception return uses non-floating-point state 
from
the PSP and execution uses PSP after return."

Diesen Wert soll man ins Link-Register setzen, welcher dann beim 
Rücksprung ins PC geladen wird. Nun weiß ich nicht ob letzteres wirklich 
funktioniert, aber im FreeRTOS ist es genau so implementiert.

Also ich gehe davon aus das der Mikrocontroller weiß das dies keine 
Adresse ist, sondern dem Kommando entspricht. So klingt es zumindenst im 
User Guide.

von Fritz (Gast)


Lesenswert?

Hallo David ich bins nochmal,

Habe mir deinen Code in die Crosswork-IDE geladen und dasselbe Problem 
wie du.
Was mir aufgefallen ist, dass du den Stackframe den du am Anfang nach 
main

    StackPointer = &Stack[127];
    /* Set xPSR to an initial value */
    *StackPointer = (unsigned long)0x01000000L;
    StackPointer--;
    /* Set PC */
    *StackPointer = (unsigned long)&Task1;
    StackPointer--;
    /* Set LR */
    *StackPointer = 0;
    /* Space for r12,r3,r2,r1,r0 */
    StackPointer -= 6;
    /* Set exec return to an initial value, return to thread mode from 
PSP, non-floating-point */
    *StackPointer = (unsigned long)0xFFFFFFFDL;
    /* Space for r11-r4 */
    StackPointer -= 8;

mit
    " svc 0    \n" /* System call to start first task. */
zerstörst.

Danach ist die &Task1-Adresse überschrieben.

Hoffe das hilft dir weiter.
Wäre nett wenn du uns weiter informierst.

von David M. (david107)


Lesenswert?

Hallo Fritz,

vielen Dank für dein Bemühen!
Du hattest Recht. Ich habe mich wohl vom Debugger beirren lassen, da die 
Adresse von Task1 trotz svc im Memory Browser hinterlegt war. Ich habe 
nun den Stack Array global definiert und jetzt klappt es!
Manchmal sieht man den Wald vor lauter Bäumen nicht... Danke

von (prx) A. K. (prx)


Lesenswert?

Fritz schrieb:
> Foglich wäre dein Wert 0xFFFFFFFD ja OK, aber laut Datenblatt vom STM
> ist dort (0xfffffffc) weder RAM noch FLASH!

Sprungbefehle in diesen Adressbereich haben eine besondere Bedeutung, 
nämlich als Exception-Return Befehle.

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.