Guten Abend,
ich versuche mich grade an einem RTOS für Cortex M (nur zum Spaß, ich
weiß es gibt viele Fertige). Mein Problem ist zur Zeit der Kontext
wechsel von einem Task zu einem anderen.
1
voidTask1(void)
2
{
3
intx=123;
4
while(1)
5
{
6
debug_printf("%d\n",x);
7
}
8
}
9
10
voidTask2(void)
11
{
12
intx=321;
13
while(1)
14
{
15
}
16
}
Als erstes Läuft Task1 und dann wird gewechselt (im Systick Handler).
Wenn Task1 dannach wieder laufen soll wird aber 321 statt 123
ausgegeben, weshalb ich befürchte, dass die Stacks durcheinander kommen.
Ich könnte mir auch gut vorstellen, dass das mit dem Software Stacking
zu tun hat, bei dem die Register R4 - R11 auf dem Stack gesichtert
werden. Die anderen Register sichert die Hardware automatisch beim
Eintritt in den Interrupt.
1
voidSaveTask(void)
2
{
3
CurrentTask->State=WAITING;
4
5
msp=__get_MSP();
6
__set_MSP(__get_PSP());
7
__ISB();
8
9
asm("push {r4}");
10
asm("push {r5}");
11
asm("push {r6}");
12
asm("push {r7}");
13
asm("push {r8}");
14
asm("push {r9}");
15
asm("push {r10}");
16
asm("push {r11}");
17
18
CurrentTask->StackPointer=__get_MSP();
19
20
__set_MSP(msp);
21
__ISB();
22
}
23
24
voidLoadTask(Task_t*task)
25
{
26
if(task->State==READY)
27
{
28
CurrentTask=task;
29
CurrentTask->State=RUNNING;
30
31
__set_PSP(CurrentTask->StackPointer);
32
__ISB();
33
}
34
elseif(task->State==WAITING)
35
{
36
CurrentTask=task;
37
CurrentTask->State=RUNNING;
38
39
msp=__get_MSP();
40
__set_MSP(CurrentTask->StackPointer);
41
__ISB();
42
43
asm("pop {r11}");
44
asm("pop {r10}");
45
asm("pop {r9}");
46
asm("pop {r8}");
47
asm("pop {r7}");
48
asm("pop {r6}");
49
asm("pop {r5}");
50
asm("pop {r4}");
51
52
CurrentTask->StackPointer=__get_MSP();
53
54
__set_MSP(msp);
55
__ISB();
56
57
__set_PSP(CurrentTask->StackPointer);
58
__ISB();
59
}
60
}
Die Stacks sind auch außreichen groß (128 Words nur für Daten) Der
Zustand READY sagt nur aus, das der Task neu ist. WAITING bedeutet, das
der Task schonmal aktiv war.
Vielleicht hat hier jemand Ahnung von RTOSs und kennt die Lösung. Per
Debugging und Step by Step konnte ich leider nicht raus finden wie die
Daten von Task2 in Task1 gelangen.
mfg Moritz
Es ist besser / übersichtlicher den Switch in Assembler zu machen, alles
im Systick Handler. Deine C Lösung könnte eventuell funktionieren, wenn
Deine Funktionen nicht selbst stacken, versuch mal:
inline void SaveTask(void)
inline void LoadTask(Task_t *task)
Ansonsten in Assembler siehe hier:
http://stackoverflow.com/questions/25199802/switching-context-inside-an-isr-on-cortex-m
Ja, so muss ein Context Switch aussehen.
Mit entsprechendem Wissen über RTOSs und die Cortex-M-Prozessoren lässt
sich hier sehr leicht verstehen, was du tust. Man merkt, dass du dir
Gedanken gemacht hast, und es ist ein schönes Stück Software entstanden.
Auf den ersten Blick finde ich hier keinen Fehler. Mich würde aber
interessieren, wie du deine Contexte initialisierst. Denn für
"if(task->State == READY)" muss bereits was im Speicher vorhanden sein,
das dann vom Interrupt Return geladen werden kann.
Dann wäre auch hilfreich zu wissen, wo genau die lokale variable x
liegt. Stack oder Register?
Ich würde mich freuen, wenn du deinen kompletten code mit der community
teilen würdest, es könnten vieleicht einige was davon lernen.
Apropos Initialisierung: benutzen die Tasks überhaupt den PSP? Zu Anfang
läuft ja alles auf dem MSP, also muss per CONTROL Register umgeschaltet
werden.
Ich verwende eine sortierte Linked-List um die Task zu speichern. Hohe
Prioritäten stehen am Anfang. TaskChain ist der Task mit der höchsten
Priorität.
Ganz am Anfang gibts eine Init-Funktion für das RTOS. Bis jetzt wird da
nur __set_CONTROL_(2) aufgerufen, was zum Process-Stack schaltet.
x ist eine lokale Variable und liegt auf dem Stack.
Wenns funktioniert lad ich das hier gerne hoch.
mfg, Moritz
Also folgendermaßen funktioniert es anscheinend (Die Task interferieren
nicht mehr):
-Alles im Systick-Handler
-Systick-Handler mit __attribute__((naked)) gekennzeichnet
-Keine Funtionsaufrufe im Handler (mit naked gibt das ein Hardfault)
Allerdings möchte ich gerne den Systick-Handler für etwas anderes
verwenden (STM32-HAL usw.).
Also hab ich den Inhalt aus dem SysTick-Handler in den PendSV kopiert
und trigger im Systick-Handler nur den PendSV (der auch mit
__attribute__((naked)) gekennzeichnet ist)
Das führt allerdings auch schon zu einem Hardfault, weil der
Systick-Handler erst beendet wird nachdem der PendSV fertig ist.
(Systick hat Priorität 0 (hoch) und PendSV Priorität 15 (niedrig)). Wenn
ich das naked attribute vom Systick wegnehme interferieren beide Task
wieder.
Was soll ich tuen? Am besten wäre es wohl wenn sich der Systick erst
komplett beendet und dann der PendSV beginnt. Aber das ist glaube ich
beim Cortex M extra so, damit alle Interrupts schnell bearbeitet werden.
mfg, Moritz
So, jetzt funktioniert es richtig. Das Problem war meine Faulheit, die
darin bestand den Simulator zu verwenden anstatt die echte Hardware. Wie
versprochen einmal das gesamte "RTOS". Um zu verstehen wie ein RTOS
funktioniert und als Grundlage für ein RTOS vielleicht ganz nützlich,
für den richtigen Einsatz fehlen noch viel zu viele Sachen. Ich hab
Crossworks verwendet (gcc) und als Hardware einen STM32F746NG. Sollte
aber mit Cortex M3 und M4 funktionen (M0 und M1 bin ich mir nicht ganz
sicher. Es wird die stm32f7xx.h mit in das RTOS eingebunden, müsste man
vllt. anpassen)
Beispiel Programm könnte so aussehen:
Ich hab mal eine Frage. Mit __set_CONTROL(2) schaltet du ja die
Nutzung des psp frei. Brauchst du dafuer ein modifiziertes startup
damit der psp korrekt initialisiert ist?
Weil ueblicherweise nutzt man Embedded doch eigentlich nur den msp oder?