Forum: Mikrocontroller und Digitale Elektronik RTOS Task Context Frage


von Christian (Gast)


Lesenswert?

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

von Benjamin U. (utzus)


Lesenswert?

> 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

von Christian (Gast)


Lesenswert?

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

von Christian (Gast)


Lesenswert?

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?

von Benjamin U. (utzus)


Lesenswert?

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

von Christian (Gast)


Lesenswert?

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.

von Benjamin U. (utzus)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Tobias Hagemeier (Gast)


Lesenswert?

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

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.