Hallo allerseits.
Eine Frage zum Task Context Switch Mechanismus(FreeRTOS):
Ein Task A muß aufgrund eines anderen Tasks B (höhere Priorität)
in den Suspend Modus. A muß nun sein Context in seinem Task Stack
speichern (für eine spätere Wiederaufnahme).
Dazu bedient sich FreeRTOS folgendem Makro.
(das Makro sieht für jede Portierung natürlich anders aus(hier AVR
Port), der grundsätzliche Mechanismus aber ist natürlich immer
derselbe, mich interessiert hier mehr der Grundsatz als AVR spezifische
Sachen)
#define portSAVE_CONTEXT() \
asm volatile ( \
push r0 \ (1)
in r0, _SREG_ \ (2)
cli \ (3)
push r0 \ (4)
push r1 \ (5)
clr r1 \ (6)
push r2 \ (7)
push r3 \
push r4 \
push r5 \
:
push r30 \
push r31 \
lds r26, pxCurrentTCB \ (8)
lds r27, pxCurrentTCB + 1 \ (9)
in r0, _SP_L_ \ (10)
st x+, r0 \ (11)
in r0, _SP_H_ \ (12)
st x+, r0 \ (13)
);
Bis zu (8) werden notwendige Register im Task Stack abgelegt.
In (8) und (9) aber wird jetzt ein Task Stack Pointer ins X Register
geladen. Es steht geschrieben(Doku):
"...pxCurrentTCB holds the address from where the tasks stack pointer
can be retrieve..."
Der Task Stack Pointer wird doch aber auch in (10)->(13), für spätere
Wiederaufnahme abgelegt.
Das verstehe ich nicht. Welcher ist jetzt nun der Task Stack Pointer?
Für eine Wiederaufnahme des Tasks A müsste doch eigentlich der Stack
Pointer Wert zum Zeitpunkt kurz vor der Unterbrechung (also (10) ->
(13))
ausreichen.
Danke für jede Antwort.
Gruß
Christian
> geladen. Es steht geschrieben(Doku):> "...pxCurrentTCB holds the address from where the tasks stack pointer> can be retrieve..."> Der Task Stack Pointer wird doch aber auch in (10)->(13), für spätere> Wiederaufnahme abgelegt.> Das verstehe ich nicht. Welcher ist jetzt nun der Task Stack Pointer?> Für eine Wiederaufnahme des Tasks A müsste doch eigentlich der Stack> Pointer Wert zum Zeitpunkt kurz vor der Unterbrechung (also (10) ->> (13))
Okay, ich muss zugeben, ich kenn mich nur begrenzt damit aus, kann mir
es aber so erklären:
Jeder Task hat ja seinen eigenen Stack, dieser wird, wie in deiner
zitierten Doku pxCurrentTCB genannt. Hiermit erreiche ich also den
Stack, der zu genau dem Task gehört. (wäre dann für jeden Task
unterschiedlich)
SP_L und SP_H sind ja die Stackpointer für den Globalen Stack, also den,
den es nur einmal gibt. Dieser wird in R0 geladen und im Pointer X
gespeichert.
Nun müsste man wissen, was passiert mit dem Pointer X danach? oder wird
er hier nun gespeichert.
Fazit: pxCurrentTCP und SP sind vermutlich (meine Vermutung) nicht die
gleichen Pointer. Ein Taskzugehöriger und ein globaler.
Zur Wiederaufnahem müsste dann der pxCurrentTCB wieder gefunden werden.
Was macht der Schedduler mit den Registern r26 und r27??
Ich hoffe, ich konnte helfen.
Grüße,
Benni
Folgendes Makro wird für Task A aufgerufen, wenn er wieder ausführbar
wird:
#define portRESTORE_CONTEXT() \
asm volatile ( \
lds r26, pxCurrentTCB \ (1)
lds r27, pxCurrentTCB + 1 \ (2)
ld r28, x+ \
out _SP_L_, r28 \ (3)
ld r29, x+ \
out _SP_H_, r29 \ (4)
pop r31 \
pop r30 \
:
pop r1 \
pop r0 \ (5)
out _SREG_, r0 \ (6)
pop r0 \ (7)
);
Verstehe aber das portRESTORE_CONTEXT() und das portSAVE_CONTEXT() Makro
noch nicht.
Auf was bezieht sich pxCurrentTCB vor der Unterbrechung und auf was
danach?
Gruß
Christian
Habe mir mal den AVR InstructionSet angeschaut(bin kein AVR Spezi):
lds r26, pxCurrentTCB \ (8)
lds r27, pxCurrentTCB + 1 \ (9)
in r0, SP_L \ (10)
st x+, r0 \ (11)
in r0, SP_H \ (12)
st x+, r0 \ (13)
Die Zeilen 8 bis13 addressieren ja indirekt. D.h. der Stackpointer
wird hier ja nach X kopiert und der pxCurrentTCB zeigt ja auf X.
Dann macht es (glaube ich) Sinn:
Bevor er (A) unterbrochen wird, wird der SP (aktuell für A) nach X
gespeichert. Nimmt A den Betrieb wieder auf, kann er auf den SP vor
der Unterbrechung durch pxCurrentTCB zugreifen.
Im Makro "portRESTORE_CONTEXT()" wird dann der SP auf den Wert von
pxCurrentTCB gesetzt. Dadurch hat A bei Neuaufnahme wieder denselben
SP wie vor der Unterbrechung.
Wäre froh, wenn mir das mal jemand bestätigt.
Wenn das so richtig ist bleibt noch eine Frage:
Zur Wiederaufnahme ist doch neben dem aktualisieren des Register Context
auch noch ein Update des PC nötig.
Oder reicht das "Register Context Saving" hier völligst aus, um ein Task
zu starten?
Christian schrieb:> Auf was bezieht sich pxCurrentTCB vor der Unterbrechung und auf was> danach?
Das bezieht sich vermutlich immer auf das gleiche. Hier wird ja, laut
deiner zitierten Dokumentation, gesagt, an dieser Stelle findet man den
taskeigenen Stack - also irgendeine Adresse.
Weißt du, wie der Taskstack implementiert ist im RTOS?
Wird da der normale Stack verwendet oder ist das einfach ein
Speicherbereich im RAM?
Benni
[EDIT]
Woher nimmst du diese Info??
> und der pxCurrentTCB zeigt ja auf X
Im Falle des Avr wird der Stack im SRAM erzeugt.
Das pxCurrentTCB auf X zeigt war nicht glücklich ausgedrückt.
Über pxCurrentTCB wird nur der SP abgelegt und auch wieder zugegriffen.
Also ein Platzhalter für die Adresse des SP.
Christian schrieb:> Im Falle des Avr wird der Stack im SRAM erzeugt.> Das pxCurrentTCB auf X zeigt war nicht glücklich ausgedrückt.> Über pxCurrentTCB wird nur der SP abgelegt und auch wieder zugegriffen.> Also ein Platzhalter für die Adresse des SP.
Achja, deswegen auch r26 und r27. Ich erinnere mich.
Vielleicht löst sich die Verwirrung, wenn man sich mal die Definition
des TCB ansieht (tskTCB => tasks.c). Dessen erste Komponente ist der
Task-Stack-Pointer, verziert mit der Bemerkung "THIS MUST BE THE FIRST
MEMBER OF THE STRUCT".
pxCurrentTCB zeigt auf den aktuellen TCB und obiger Code schreibt/liest
ebendiese erste Komponente des TCB. Und weil er das in Assembler tut,
und dabei einen Offset von 0 annimmt, steht der erwähnte Kommentar da
dran.
Zum "starten" des Tasks:
Der Task-Kontext wird entweder vor einem Interrupt-Handler oder in einer
aufrufenden Funktion gesichert (wenn der Task freiwillig pausiert). In
beiden Fällen ist die Rücksprungadresse die in den Task zurück führt
bereits auf den Stack gesichert worden (entweder durch den µC selbst
beim Interrupt oder durch den Task beim Aufrufen der Funktion).
Es reicht also dann, den Kontext des Tasks wiederherzustellen und mit
RET oder RETI zur ursprünglichen Position zu springen, die Adresse liegt
ja auf dem Stack.
Gruß,
Tobi